如何验证通过cookie传递的JWT?

如何验证通过cookie传递的JWT?,第1张

概述ASP.NET Core中的UseJwtBearerAuthentication中间件可以轻松验证授权标头中的传入 JSON Web令牌. 如何验证通过cookie而不是标头传递的JWT?类似于UseCookieAuthentication,但是对于只包含JWT的cookie. 我建议你看一下以下的链接. https://stormpath.com/blog/token-authenticatio ASP.NET Core中的UseJwtBearerAuthentication中间件可以轻松验证授权标头中的传入 JSON Web令牌.

如何验证通过cookie而不是标头传递的JWT?类似于UsecookieAuthentication,但是对于只包含JWT的cookie.

解决方法 我建议你看一下以下的链接.

https://stormpath.com/blog/token-authentication-asp-net-core

它们将JWT令牌存储在仅http的cookie中以防止XSS攻击.

然后,他们通过在Startup.cs中添加以下代码来验证cookie中的JWT令牌:

app.UsecookieAuthentication(new cookieAuthenticationoptions{    automaticAuthenticate = true,automaticChallenge = true,AuthenticationScheme = "cookie",cookiename = "access_token",TicketDataFormat = new CustomJwtDataFormat(        SecurityAlgorithms.HmacSha256,tokenValIDationParameters)});

CustomJwtDataFormat()是这里定义的自定义格式:

public class CustomJwtDataFormat : ISecureDataFormat<AuthenticationTicket>{    private Readonly string algorithm;    private Readonly TokenValIDationParameters valIDationParameters;    public CustomJwtDataFormat(string algorithm,TokenValIDationParameters valIDationParameters)    {        this.algorithm = algorithm;        this.valIDationParameters = valIDationParameters;    }    public AuthenticationTicket Unprotect(string protectedText)        => Unprotect(protectedText,null);    public AuthenticationTicket Unprotect(string protectedText,string purpose)    {        var handler = new JwtSecurityTokenHandler();        ClaimsPrincipal principal = null;        SecurityToken valIDToken = null;        try        {            principal = handler.ValIDatetoken(protectedText,this.valIDationParameters,out valIDToken);            var valIDJwt = valIDToken as JwtSecurityToken;            if (valIDJwt == null)            {                throw new ArgumentException("InvalID JWT");            }            if (!valIDJwt.header.Alg.Equals(algorithm,StringComparison.Ordinal))            {                throw new ArgumentException($"Algorithm must be '{algorithm}'");            }            // Additional custom valIDation of JWT claims here (if any)        }        catch (SecurityTokenValIDationException)        {            return null;        }        catch (ArgumentException)        {            return null;        }        // ValIDation passed. Return a valID AuthenticationTicket:        return new AuthenticationTicket(principal,new AuthenticationPropertIEs(),"cookie");    }    // This ISecureDataFormat implementation is decode-only    public string Protect(AuthenticationTicket data)    {        throw new NotImplementedException();    }    public string Protect(AuthenticationTicket data,string purpose)    {        throw new NotImplementedException();    }}

另一个解决方案是编写一些自定义中间件来拦截每个请求,查看它是否有cookie,从cookie中提取JWT并在它到达控制器的Authorize过滤器之前动态添加Authorization标头.以下是一些适用于OAuth令牌的代码,以获取想法:

using System.Threading.Tasks;using Microsoft.AspNetCore.http;using Microsoft.Extensions.Logging;namespace MIDdlewareSample{    public class JWTInheaderMIDdleware    {        private Readonly RequestDelegate _next;        public JWTInheaderMIDdleware(RequestDelegate next)        {            _next = next;        }        public async Task Invoke(httpContext context)        {           var authenticationcookiename = "access_token";           var cookie = context.Request.cookies[authenticationcookiename];           if (cookie != null)           {               var token = JsonConvert.DeserializeObject<Accesstoken>(cookie);               context.Request.headers.Append("Authorization","Bearer " + token.access_token);           }           await _next.Invoke(context);        }    }}

…其中Accesstoken是以下类:

public class Accesstoken{    public string token_type { get; set; }    public string access_token { get; set; }    public string expires_in { get; set; }}

希望这可以帮助.

注意:同样重要的是要注意这种做事方式(仅限http的cookie中的令牌)将有助于防止XSS攻击但是不能免受跨站点请求伪造(CSRF)攻击,因此您必须使用防伪令牌或设置自定义标头以防止这些.

此外,如果您不进行任何内容清理,攻击者仍然可以运行XSS脚本代表用户发出请求,即使启用了仅http cookie和CRSF保护.但是,攻击者无法窃取仅包含令牌的http的cookie,攻击者也无法从第三方网站发出请求.

因此,您仍应对用户生成的内容(如评论等)执行大量清理工作.

编辑:在评论中写道,博客文章链接和代码是由OP自己在几天前提出这个问题后编写的.

对于那些对另一种“cookie中的令牌”感兴趣的方法来减少XSS暴露,他们可以使用oAuth中间件,例如ASP.NET Core中的OpenID Connect Server.

在被调用以将令牌(ApplyTokenResponse())发送回客户端的令牌提供程序的方法中,您可以序列化令牌并将其存储到仅限http的cookie中:

using System.Security.Claims;using System.Threading.Tasks;using AspNet.Security.OpenIDConnect.Extensions;using AspNet.Security.OpenIDConnect.Server;using Newtonsoft.Json;namespace Shared.ProvIDers{public class AuthenticationProvIDer : OpenIDConnectServerProvIDer{    private Readonly Iapplicationservice _applicationservice;    private Readonly IUserService _userService;    public AuthenticationProvIDer(IUserService userService,Iapplicationservice applicationservice)    {        _applicationservice = applicationservice;        _userService = userService;    }    public overrIDe Task ValIDatetokenRequest(ValIDatetokenRequestContext context)    {        if (string.IsNullOrEmpty(context.ClIEntID))        {            context.Reject(                error: OpenIDConnectConstants.Errors.InvalIDRequest,description: "Missing credentials: ensure that your credentials were correctly " +                             "flowed in the request body or in the authorization header");            return Task.Fromresult(0);        }        #region ValIDate ClIEnt        var application = _applicationservice.GetByClIEntID(context.ClIEntID);            if (applicationResult == null)            {                context.Reject(                            error: OpenIDConnectConstants.Errors.InvalIDClIEnt,description: "Application not found in the database: ensure that your clIEnt_ID is correct");                return Task.Fromresult(0);            }            else            {                var application = applicationResult.Data;                if (application.ApplicationType == (int)ApplicationTypes.JavaScript)                {                    // Note: the context is marked as skipped instead of valIDated because the clIEnt                    // is not trusted (JavaScript applications cannot keep their credentials secret).                    context.Skip();                }                else                {                    context.Reject(                            error: OpenIDConnectConstants.Errors.InvalIDClIEnt,description: "Authorization server only handles JavaScript application.");                    return Task.Fromresult(0);                }            }        #endregion ValIDate ClIEnt        return Task.Fromresult(0);    }    public overrIDe async Task HandletokenRequest(HandletokenRequestContext context)    {        if (context.Request.IsPasswordGrantType())        {            var username = context.Request.Username.TolowerInvariant();            var user = await _userService.GetUserLoginDtoAsync(                // filter                u => u.Username == username            );            if (user == null)            {                context.Reject(                        error: OpenIDConnectConstants.Errors.InvalIDGrant,description: "InvalID username or password.");                return;            }            var password = context.Request.Password;            var passWordCheckResult = await _userService.CheckUserPasswordAsync(user,context.Request.Password);            if (!passWordCheckResult)            {                context.Reject(                        error: OpenIDConnectConstants.Errors.InvalIDGrant,description: "InvalID username or password.");                return;            }            var roles = await _userService.GetUserRolesAsync(user);            if (!roles.Any())            {                context.Reject(                        error: OpenIDConnectConstants.Errors.InvalIDRequest,description: "InvalID user configuration.");                return;            }        // add the claims        var IDentity = new ClaimsIDentity(context.Options.AuthenticationScheme);        IDentity.AddClaim(ClaimTypes.nameIDentifIEr,user.ID,OpenIDConnectConstants.Destinations.Accesstoken,OpenIDConnectConstants.Destinations.IDentityToken);        IDentity.AddClaim(ClaimTypes.name,user.Username,OpenIDConnectConstants.Destinations.IDentityToken);         // add the user's roles as claims        foreach (var role in roles)        {            IDentity.AddClaim(ClaimTypes.Role,role,OpenIDConnectConstants.Destinations.IDentityToken);        }         context.ValIDate(new ClaimsPrincipal(IDentity));        }        else        {            context.Reject(                    error: OpenIDConnectConstants.Errors.InvalIDGrant,description: "InvalID grant type.");            return;        }        return;    }    public overrIDe Task ApplyTokenResponse(ApplyTokenResponseContext context)    {        var token = context.Response.Root;        var stringifIEd = JsonConvert.SerializeObject(token);        // the token will be stored in a cookie on the clIEnt        context.httpContext.Response.cookies.Append(            "exampletoken",stringifIEd,new Microsoft.AspNetCore.http.cookieOptions()            {                Path = "/",httpOnly = true,// to prevent XSS                Secure = false,// set to true in production                Expires = // your token life time            }        );        return base.ApplyTokenResponse(context);    }}}

然后,您需要确保每个请求都附加了cookie.您还必须编写一些中间件来拦截cookie并将其设置为标头:

public class Authorizationheader{    private Readonly RequestDelegate _next;    public Authorizationheader(RequestDelegate next)    {        _next = next;    }    public async Task Invoke(httpContext context)    {        var authenticationcookiename = "exampletoken";        var cookie = context.Request.cookies[authenticationcookiename];        if (cookie != null)        {            if (!context.Request.Path.ToString().Tolower().Contains("/account/logout"))            {                if (!string.IsNullOrEmpty(cookie))                {                    var token = JsonConvert.DeserializeObject<Accesstoken>(cookie);                    if (token != null)                    {                        var headerValue = "Bearer " + token.access_token;                        if (context.Request.headers.ContainsKey("Authorization"))                        {                            context.Request.headers["Authorization"] = headerValue;                        }else                        {                            context.Request.headers.Append("Authorization",headerValue);                        }                    }                }                await _next.Invoke(context);            }            else            {                // this is a logout request,clear the cookie by making it expire Now                context.Response.cookies.Append(authenticationcookiename,"",new Microsoft.AspNetCore.http.cookieOptions()                                                {                                                    Path = "/",Secure = false,Expires = DateTime.UtcNow.AddHours(-1)                                                });                context.Response.Redirect("/");                return;            }        }        else        {            await _next.Invoke(context);        }    }}

在startup.cs的Configure()中:

// use the Authorizationheader mIDdleware    app.UseMIDdleware<Authorizationheader>();    // Add a new mIDdleware valIDating access tokens.    app.USEOAuthValIDation();

然后,您可以正常使用“授权”属性.

[Authorize(Roles = "administrator,User")]

此解决方案适用于API和mvc应用程序.但是对于AJAX和fetch请求,你必须编写一些自定义中间件,它不会将用户重定向到登录页面而是返回401:

public class RedirectHandler{    private Readonly RequestDelegate _next;    public RedirectHandler(RequestDelegate next)    {        _next = next;    }    public bool IsAJAXRequest(httpContext context)    {        return context.Request.headers["X-Requested-With"] == "XMLhttpRequest";    }    public bool IsFetchRequest(httpContext context)    {        return context.Request.headers["X-Requested-With"] == "Fetch";    }    public async Task Invoke(httpContext context)    {        await _next.Invoke(context);        var AJAX = IsAJAXRequest(context);        var fetch = IsFetchRequest(context);        if (context.Response.StatusCode == 302 && (AJAX || fetch))        {            context.Response.Clear();            context.Response.StatusCode = (int)httpStatusCode.Unauthorized;            await context.Response.WriteAsync("Unauthorized");            return;        }    }}
总结

以上是内存溢出为你收集整理的如何验证通过cookie传递的JWT?全部内容,希望文章能够帮你解决如何验证通过cookie传递的JWT?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1138469.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-30
下一篇2022-05-30

发表评论

登录后才能评论

评论列表(0条)

    保存