ASP.NET JWT 不记名身份验证不更改请求上下文中的用户

Posted

技术标签:

【中文标题】ASP.NET JWT 不记名身份验证不更改请求上下文中的用户【英文标题】:ASP.NET JWT bearer authentication not changing user in request context 【发布时间】:2017-04-10 17:12:48 【问题描述】:

我正在为我的 SPA 创建一个简单的 OAuth 服务器,并遵循 this 教程。我可以向 OAuth 提供者请求 JWT 罚款。但是,当我在授权标头中发送 JWT(例如:Authorization Bearer <token>)时,我总是收到 401“此请求的授权已被拒绝”。

单步执行代码,我可以看到我在请求上下文中的用户设置为匿名 Windows 身份验证用户,而不是 JWT 的所有者。

我看到很多人说要在 OAuth 配置之后调用 app.UseWebAPI(),但我没有在我的 startup.cs 中注册 WebAPI。我可以做?我目前的代码中没有app.UseWebAPI()(API 无需授权即可正常工作)。

这是我的 startup.cs:

public partial class Startup
    
        public void Configuration(IAppBuilder app)
        
            ConfigureOAuth(app);
        
    

public partial class Startup
    
        /// <summary>
        /// OAuth configuration
        /// </summary>
        /// <param name="app"></param>
        public void ConfigureOAuth(IAppBuilder app)
        
            // Unique identifier for the entity issuing the token
            var issuer = ConfigurationManager.AppSettings["issuer"];
            // Private key used to secure the token
            var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);

            // Enable JWT authentication
            app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
            
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[]  "Any" ,
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                
                    new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                
            );

            // Create authentication endponit
            app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
            
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/api/login/"),
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
                Provider = new CustomOAuthProvider(),
                AccessTokenFormat = new CustomJwtFormat(issuer)
            );
        
    

这是我的 CustomOAuthProvider 类:

public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    
        IMessengerRepository repo;

        public CustomOAuthProvider() : base()
        
            repo = new MessengerRepository();
        

        public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        
            // Enable CORS
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[]  "*" );

            // Check against the repository to see if the login details are correct
            var user = repo.Authenticate(context.UserName, context.Password);

            // If the login details are incorrect, return an error
            if (user == null)
            
                context.SetError("invalid_grant", "The username or password is incorrect");
                context.Rejected();
                return Task.FromResult<object>(null);
            

            // Otherwise, return a JWT
            var ticket = new AuthenticationTicket(SetClaimsIdentity(context, user), new AuthenticationProperties());
            context.Validated(ticket);

            return Task.FromResult<object>(null);
        

        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        
            // Because the audience is not validated, the request is validated straight away without any checks
            context.Validated();
            return Task.FromResult<object>(null);
        

        /// <summary>
        /// Create a ClaimsIdentity based on their username and role
        /// </summary>
        /// <param name="context"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, UserDto user)
        
            var identity = new ClaimsIdentity("JWT");

            // Add claims for the user themselves
            identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            identity.AddClaim(new Claim("sub", context.UserName));
            // Add claim for the user's role
            identity.AddClaim(new Claim(ClaimTypes.Role, user.UserRole.RoleName.TrimEnd(' ')));

            return identity;
        
    

还有我的 CustomJwtFormat 类:

public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    
        // Private key for encoding token
        private static readonly byte[] secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
        // Entity issuing the token
        private readonly string issuer;

        public CustomJwtFormat(string issuer)
        
            this.issuer = issuer;
        

        public string Protect(AuthenticationTicket data)
        
            if (data == null)
                throw new ArgumentNullException(nameof(data));

            var signingKey = new HmacSigningCredentials(secret);
            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;

            return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(issuer, null, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey));
        

        public AuthenticationTicket Unprotect(string protectedText)
        
            throw new NotImplementedException();
        
    

【问题讨论】:

【参考方案1】:

对于新读者,UseJwtBearerAuthentication() 已过时。 https://docs.microsoft.com/es-es/dotnet/api/microsoft.aspnetcore.builder.jwtbearerappbuilderextensions.usejwtbearerauthentication?view=aspnetcore-2.2

【讨论】:

【参考方案2】:

艾萨克,

您的实现看起来不错,但audience id 存在一个小问题。

在您的 CustomJwtFormat for authorization 中,您将 null 指定为受众 ID。而对于authentication,您指定Any。 这就是您获得401 Unauthorized 的原因。

如果您使用以下内容更改您的 JwtSecurityToken,它将起作用。

return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(issuer, "Any", data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey));

【讨论】:

以上是关于ASP.NET JWT 不记名身份验证不更改请求上下文中的用户的主要内容,如果未能解决你的问题,请参考以下文章

在 asp.net vnext 上使用不记名令牌身份验证刷新令牌

如何对 ASP.NET WebApi 的每个请求应用自定义验证到 JWT 令牌?

JWT 不记名令牌不适用于 ASP.Net Core 3.1 + Identity Server 4

ASP.NET Web API 的 JWT 身份验证

ASP.NET Web API 的 JWT 身份验证

ASP.NET Web API 的 JWT 身份验证