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 令牌?