
如何验证通过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?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)