来自 C# HttpClient 针对 Spring 服务器 JWT 令牌的身份验证

Posted

技术标签:

【中文标题】来自 C# HttpClient 针对 Spring 服务器 JWT 令牌的身份验证【英文标题】:Authentication from C# HttpClient against Spring server JWT Tokens 【发布时间】:2016-08-22 07:42:46 【问题描述】:

我正在编写 Xamarin 跨平台移动应用程序。该服务器是一个 SpringMVC 服务器,它使用 JWT 令牌对每个端点/Web 服务进行身份验证。所以基本上当我第一次向网络服务发出请求时,在我需要点击/authorize POST 端点发送我的电子邮件和密码之前,端点响应将在"Cookie" 标头中包含一个身份验证令牌,它是"AUTH_TOKEN=MD5-String"。获得令牌后,我将请求发送到端点,比如说/feed。但我的问题是我无法弄清楚在 C#HttpClient 中设置“Cookie”标头的方法。我尝试了所有方法,但 endpoing 只是继续使用登录屏幕 html 响应,而不是实际的 JSON 响应。我在 Postman 和其他 REST 客户端中尝试了相同的步骤,并且成功了。所以这意味着我做错了什么。这是我的代码:

public class RestService : IRestService

    HttpClient client;
    HttpClientHandler handler;
    CookieContainer cookies;
    string authToken;

    public List<Feed> FeedItems  get; private set; 

    public RestService()
    
        cookies = new CookieContainer();
        handler = new HttpClientHandler();
        handler.UseCookies = true; //Otherwise It'll not use the cookies container!!
        handler.CookieContainer = cookies;
        client = new HttpClient(handler);
        client.MaxResponseContentBufferSize = 256000;
    

    public async Task<List<Role>> GetFeedDataAsync()
    
        //Request credentials
        //Credentials validation
        var credentials = new HalliganCredential()
        
            email = Constants.Username,
            password = Constants.Password
        ;
        var jsonCredentials = JsonConvert.SerializeObject(credentials);
        var jsonCredentialsContent = new StringContent(jsonCredentials, Encoding.UTF8, "application/json");
        var authorizeUri = new Uri(Constants.AuthorizeEndpoint);
        var authorizeResponse = await client.PostAsync(authorizeUri, jsonCredentialsContent);
        if (authorizeResponse.IsSuccessStatusCode)
        
            //If authentication went OK
            IEnumerable<Cookie> responseCookies = cookies.GetCookies(authorizeUri).Cast<Cookie>();
            foreach (Cookie cookie in responseCookies)
            
                if (cookie.Name.Equals("AUTH-TOKEN"))
                
                    authToken = cookie.Value;
                
            
        
        else
        
            //Authentication failed throw error
            throw new HttpRequestException("Authentication failed");
        


        FeedItems = new List<Feed>();

        //Making the GET request
        var uri = new Uri(string.Format(Constants.FeedEnpoint, string.Empty));
        try
        
            cookies.Add(uri, new Cookie("Cookie", string.Format("AUTH_TOKEN=0", authToken)));
            client.DefaultRequestHeaders.Add("Cookie", string.Format("AUTH_TOKEN=0", authToken));
            handler.CookieContainer.Add(uri, new Cookie("Cookie", string.Format("AUTH_TOKEN=0", authToken)));

            var response = await client.GetAsync(uri);
            response.EnsureSuccessStatusCode();
            //Credentials validation
            if (response.IsSuccessStatusCode)
            
                var content = await response.Content.ReadAsStringAsync();
                FeedItems = JsonConvert.DeserializeObject<List<Feed>>(content);
            
        
        catch (Exception ex)
        
            Debug.WriteLine(@"ERROR 0", ex.Message);
        


        return FeedItems;
    

当我到达var content = await response.Content.ReadAsStringAsync(); 行时,响应是一个 HTML 字符串,而不是实际的 JSON 响应。

尽管"Cookie" 是在 Postman 上工作的,但我尝试使用其他几个键值作为标题。

我尝试使用"Set-Cookie""set-cookie""Set-Cookie",将标题设置为"AUTH_TOKEN"。我在不同的地方尝试了所有这些建议,例如将它们添加到 cookie CookieContainerhandler CookieContainerclient.DefaultRequestHeaders 中。

我尝试打开和关闭handler.UseCookies = true; //Otherwise It'll not use the cookies container!! 线。

欢迎任何帮助!

更新

我尝试了建议的解决方案之一,但没有成功我尝试了 UseCookies 的真假。

         //Making the GET request
        var baseAddress = new Uri("http://app.******.io");
        using (var handler = new HttpClientHandler  UseCookies = true )
        using (var client = new HttpClient(handler)  BaseAddress = baseAddress )
        
            var message = new HttpRequestMessage(HttpMethod.Get, "/api/v1/feed?api_key=sarasa");
            message.Headers.Add("Cookie", string.Format("AUTH_TOKEN=0", authToken));
            message.Headers.Add("Cookie", string.Format("AUTH_TOKEN=0;", authToken));
            message.Headers.Add("Set-Cookie", string.Format("AUTH_TOKEN=0", authToken));
            message.Headers.Add("AUTH_TOKEN", authToken);
            var result = await client.SendAsync(message);
            result.EnsureSuccessStatusCode();
            if (result.IsSuccessStatusCode)
            
                var content = await result.Content.ReadAsStringAsync();
                FeedItems= JsonConvert.DeserializeObject<List<Feed>>(content);
            
        
        return FeedItems;

更新

我尝试了另一种解决方案,结果相同。

var baseAddress = new Uri("http://app.*****.io");
            var cookieContainer = new CookieContainer();
            using (var handler = new HttpClientHandler()  CookieContainer = cookieContainer )
            using (var client = new HttpClient(handler)  BaseAddress = baseAddress )
            
                cookieContainer.Add(baseAddress, new Cookie("Cookie", string.Format("AUTH_TOKEN=0", authToken)));
                cookieContainer.Add(baseAddress, new Cookie("Set-Cookie", string.Format("AUTH_TOKEN=0", authToken)));
                var result = client.GetAsync("/api/v1/roles?api_key=sarasa").Result;
                result.EnsureSuccessStatusCode();
                if (result.IsSuccessStatusCode)
                
                    var content = await result.Content.ReadAsStringAsync();
                    RolesItems = JsonConvert.DeserializeObject<List<Role>>(content);
                
            

HttpClient 有替代品吗?

【问题讨论】:

How do I set a cookie on HttpClient's HttpRequestMessage的可能重复 试试:***.com/a/13287224/4984832 @SushiHangover 查看我更新的问题。我尝试了该解决方案但没有用,我将尝试使用其他解决方案,看看它是否有效...... 仅供参考:在该示例中,您实际上在 HttpClientHandler 中设置了 UseCookies = false,然后手动将它们添加到标题中(就像您正在做的那样) 是的,但我说我尝试了这两个值... 【参考方案1】:

我终于可以设置 Cookie 标头参数了,但我将 HttpClient 更改为 HttpWebRequest

获取 Cookies

//Credentials validation
            var credentials = new CompanyCredential()
            
                Email = Constants.Username,
                Password = Constants.Password
            ;
            var jsonCredentials = JsonConvert.SerializeObject(credentials);
            var request = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress, Constants.AuthorizeEndpoint));
            request.ContentType = "application/json";
            request.Method = "POST";
            var requestStream = request.GetRequestStreamAsync().Result;
            var streamWriter = new StreamWriter(requestStream);
            streamWriter.Write(jsonCredentials);
            streamWriter.Flush();
            try
            
                HttpWebResponse response = (HttpWebResponse) request.GetResponseAsync().Result;
                if (response.StatusCode.Equals(HttpStatusCode.OK))
                
                    authToken = response.Headers["Set-Cookie"];
                    tokenExpireDate = DateTime.ParseExact(response.Headers["Expires"], "yyyy-MM-dd HH:mm:ss,fff",
                                       System.Globalization.CultureInfo.InvariantCulture);
                
                else
                
                    //Authentication failed throw error
                    throw new HttpRequestException("Authentication failed");
                
             catch (Exception e)
            
                Debug.WriteLine(string.Format("Warning: 0", e.Message));
            

设置 Cookies

var request = (HttpWebRequest)WebRequest.Create(new Uri(baseAddress, endpoint));
            SetHeaders(request);
            if (string.IsNullOrEmpty(authToken))
            
                throw new AuthTokenNullException();
            
            request.Headers["Cookie"] = authToken;
            request.Method = "GET";
            HttpWebResponse response = request.GetResponseAsync().Result as HttpWebResponse;
            if (!response.StatusCode.Equals(HttpStatusCode.OK))
            
                throw new HttpRequestException(string.Format("Warning expected response as 200 and got 0", Convert.ToString(response.StatusCode)));
            
            var reader = new StreamReader(response.GetResponseStream());
            string stringResponse = reader.ReadToEnd();
            return JsonConvert.DeserializeObject<T>(stringResponse);

【讨论】:

以上是关于来自 C# HttpClient 针对 Spring 服务器 JWT 令牌的身份验证的主要内容,如果未能解决你的问题,请参考以下文章

Fiddler 没有看到来自 C# HttpClient() 的 API 调用

来自 Azure 函数的 C# HttpClient POST 请求,带有用于第三方 API 的授权标记,被剥离了标头和正文

无法在 C# 中使用 httpclient 获取标头“Content-Disposition”

调试器在解析 Rest API 响应 HttpClient、Xamarin Forms 时停止

C#关于HttpClient的统一配置

C# 中的 HttpClient 多部分表单发布