我正在使用ASP.NET MVC核心作为后端API开发Angular SPA,我正在使用它 在让我的JWT身份验证正常工作方面遇到了很多麻烦。
我使用Openiddict作为我的JWT中间件来发行令牌。我可以成功向我的控制器发送令牌请求,将用户登录,然后将令牌发送回客户端。
然而,当我尝试访问受保护的API路由时,我收到了401未经授权的消息。我可以看到令牌正在请求的标头中发送,所以它不能在服务器端正确读取,所以我认为这只是一个配置问题。我为此查看过的所有资源都使得它似乎没有任何其他配置可行。
以下是代码的相关部分:
启动配置服务
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization();
services.AddEntityFramework()
.AddEntityFrameworkSqlServer()
.AddDbContext<DbContext>();
services.AddIdentity<User, IdentityRole>(config =>
{
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
})
.AddEntityFrameworkStores<DbContext>()
.AddDefaultTokenProviders();
services.AddOpenIddict<DbContext>()
.AddMvcBinders()
.EnableTokenEndpoint("/auth/token")
.UseJsonWebTokens()
.AllowPasswordFlow()
.AddEphemeralSigningKey()
//.AddSigningCertificate()
.DisableHttpsRequirement();
services.AddLogging();
}
启动配置应用
public void Configure(IApplicationBuilder app, RequestJockeyDataSeeder seeder, ILoggerFactory factory)
{
app.UseDefaultFiles(new DefaultFilesOptions()
{
DefaultFileNames = new[] { "index.html" }
});
app.UseStaticFiles();
app.UseOpenIddict();
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Audience = "http://localhost:5000",
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
});
app.UseMvc();
}
控制器 - 我从网上的一个例子中得到了这个
[HttpPost("~/auth/token"), Produces("application/json")]
public async Task<IActionResult> Token(OpenIdConnectRequest request)
{
if (!request.IsPasswordGrantType())
{
// Return bad request if the request is not for password grant type
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
}
var user = await _userManager.FindByNameAsync(request.Username);
if (user == null)
{
// Return bad request if the user doesn't exist
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "Invalid username or password"
});
}
// Check that the user can sign in and is not locked out.
// If two-factor authentication is supported, it would also be appropriate to check that 2FA is enabled for the user
if (!await _signInManager.CanSignInAsync(user) || (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)))
{
// Return bad request is the user can't sign in
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user cannot sign in."
});
}
if (!await _userManager.CheckPasswordAsync(user, request.Password))
{
// Return bad request if the password is invalid
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "Invalid username or password"
});
}
// The user is now validated, so reset lockout counts, if necessary
if (_userManager.SupportsUserLockout)
{
await _userManager.ResetAccessFailedCountAsync(user);
}
// Create the principal
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Claims will not be associated with specific destinations by default, so we must indicate whether they should
// be included or not in access and identity tokens.
foreach (var claim in principal.Claims)
{
// For this sample, just include all claims in all token types.
// In reality, claims' destinations would probably differ by token type and depending on the scopes requested.
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
}
// Create a new authentication ticket for the user's principal
var ticket = new AuthenticationTicket(
principal,
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
// Include resources and scopes, as appropriate
var scope = new[]
{
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles
}.Intersect(request.GetScopes());
ticket.SetResources("http://localhost:5000/");
ticket.SetScopes(scope);
ticket.Properties.ExpiresUtc = DateTimeOffset.Now.AddMinutes(15);
// Sign in the user
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
我将省略角度的http拦截器,因为路由通过应用程序以及Postman都失败了。在配置MVC以正确处理这些令牌时,我是否还有什么遗漏?
答案 0 :(得分:-1)
在启用日志记录并查看更详细的错误后,结束解决此问题。
我将JwtBearerAuthentication更新为:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.28.0/js/jquery.tablesorter.min.js"></script>
<table id="myTable" class="tablesorter">
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Email</th>
<th>Due</th>
<th>Web Site</th>
</tr>
</thead>
<tbody>
<tr>
<td>Smith</td>
<td>John</td>
<td>jsmith@gmail.com</td>
<td>$50.00</td>
<td>http://www.jsmith.com</td>
</tr>
<tr>
<td>Bach</td>
<td>Frank</td>
<td>fbach@yahoo.com</td>
<td>$50.00</td>
<td>http://www.frank.com</td>
</tr>
<tr>
<td>Doe</td>
<td>Jason</td>
<td>jdoe@hotmail.com</td>
<td>$100.00</td>
<td>http://www.jdoe.com</td>
</tr>
<tr>
<td>Conway</td>
<td>Tim</td>
<td>tconway@earthlink.net</td>
<td>$50.00</td>
<td>http://www.timconway.com</td>
</tr>
</tbody>
</table>
主要变化是 app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Audience = "http://localhost:5000/",
Authority = "http://localhost:5000/",
RequireHttpsMetadata = false,
TokenValidationParameters = new TokenValidationParameters() {
ValidateIssuerSigningKey = false
}
}