我正在使用 ASP.NET Core 构建授权/身份验证 Angular2 应用程序。
当我使用 Authorize 属性调用方法时,在 Visual Studio 输出窗口中出现错误:
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:50373/api/tasks/GetLatest/ text/plain
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Failed to validate the token eyJhbGciOiJSUzI1NiIsImtpZCI6IlJHRUVHM0FCSUJNVllHTEdQSjFSNjNRRkdHQlVKRUlHLU9ITy1QREgiLCJ0eXAiOiJKV1QifQ.eyJ1bmlxdWVfbmFtZSI6Im1hdGphei5jb2ZAb3BhbC5zaSIsIkFzcE5ldC5JZGVudGl0eS5TZWN1cml0eVN0YW1wIjoiMWVhYWE4MzktNzZlNy00NmVhLWE2NGUtYWJmNDVhNzY5YTBiIiwicm9sZSI6IkFkbWluaXN0cmF0b3JzIiwianRpIjoiM2U1NjAyMjUtMzQyNy00YmE4LTg4MzQtYzA2ZGI4NDE2NDA0IiwidXNhZ2UiOiJhY2Nlc3NfdG9rZW4iLCJzY29wZSI6WyJvcGVuaWQiLCJvZmZsaW5lX2FjY2VzcyJdLCJzdWIiOiIxIiwibmJmIjoxNDg1NzgyOTAwLCJleHAiOjE0ODU3ODQ3MDAsImlhdCI6MTQ4NTc4MjkwMCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDM3My8ifQ.IqDnWJJKRtAh0BWKcdQgg_Gm_rnarffdnq5ksqpm0m6BnslI0EZ-7CugSG0223k6_FwuU7iSCiI2Cu7O2uaUedDEOdoIwyxpaEyr6yjzT5pxw7hbMnNxmxAydEajqE3OI-C55vzK0nhv9ToS93dz_QF8MIQ5EMIJ1cGXXO9wqQQ0xLvX7o2wIlM3rYvh_OORdALBxl5byMsrtc3ZrVj-BEiYuuwrUwSU5oPlH28o_Oo030s9NGqaQNea5T3PNQAL8-qC6aIdcDLBwOYZevTGGLMxde9czk_Duc0mkp5KtsyVZ-oV3qTh-EdZxpPjttu5_5Bh-8YCDLP5AzOsyX5sbg.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null.
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwt, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext()
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Bearer was not authenticated. Failure message: IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
在此之前,我登录自己并在 Angular2 应用程序中获取以下对象:
access_token:"eyJhbGciOiJSUzI1NiIsImtpZCI6IlJHRUVHM0FCSUJNVllHTEdQSjFSNjNRRkdHQlVKRUlHLU9ITy1QREgiLCJ0eXAiOiJKV1QifQ.eyJ1bmlxdWVfbmFtZSI6Im1hdGphei5jb2ZAb3BhbC5zaSIsIkFzcE5ldC5JZGVudGl0eS5TZWN1cml0eVN0YW1wIjoiMWVhYWE4MzktNzZlNy00NmVhLWE2NGUtYWJmNDVhNzY5YTBiIiwicm9sZSI6IkFkbWluaXN0cmF0b3JzIiwianRpIjoiM2U1NjAyMjUtMzQyNy00YmE4LTg4MzQtYzA2ZGI4NDE2NDA0IiwidXNhZ2UiOiJhY2Nlc3NfdG9rZW4iLCJzY29wZSI6WyJvcGVuaWQiLCJvZmZsaW5lX2FjY2VzcyJdLCJzdWIiOiIxIiwibmJmIjoxNDg1NzgyOTAwLCJleHAiOjE0ODU3ODQ3MDAsImlhdCI6MTQ4NTc4MjkwMCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDM3My8ifQ.IqDnWJJKRtAh0BWKcdQgg_Gm_rnarffdnq5ksqpm0m6BnslI0EZ-7CugSG0223k6_FwuU7iSCiI2Cu7O2uaUedDEOdoIwyxpaEyr6yjzT5pxw7hbMnNxmxAydEajqE3OI-C55vzK0nhv9ToS93dz_QF8MIQ5EMIJ1cGXXO9wqQQ0xLvX7o2wIlM3rYvh_OORdALBxl5byMsrtc3ZrVj-BEiYuuwrUwSU5oPlH28o_Oo030s9NGqaQNea5T3PNQAL8-qC6aIdcDLBwOYZevTGGLMxde9czk_Duc0mkp5KtsyVZ-oV3qTh-EdZxpPjttu5_5Bh-8YCDLP5AzOsyX5sbg"
expiration_date:"1485784700807"
expires_in:1800
id_token:"eyJhbGciOiJSUzI1NiIsImtpZCI6IlJHRUVHM0FCSUJNVllHTEdQSjFSNjNRRkdHQlVKRUlHLU9ITy1QREgiLCJ0eXAiOiJKV1QifQ.eyJ1bmlxdWVfbmFtZSI6Im1hdGphei5jb2ZAb3BhbC5zaSIsIkFzcE5ldC5JZGVudGl0eS5TZWN1cml0eVN0YW1wIjoiMWVhYWE4MzktNzZlNy00NmVhLWE2NGUtYWJmNDVhNzY5YTBiIiwicm9sZSI6IkFkbWluaXN0cmF0b3JzIiwic3ViIjoiMSIsImp0aSI6IjFmMTAzODI1LTAyM2ItNDAwNy05NjI3LTI4ODk0MmZmNzM5NyIsInVzYWdlIjoiaWRlbnRpdHlfdG9rZW4iLCJhdF9oYXNoIjoiRGVXaUcwbklJWXVxcW9vUVlBclQ3USIsIm5iZiI6MTQ4NTc4MjkwMCwiZXhwIjoxNDg1Nzg0MTAwLCJpYXQiOjE0ODU3ODI5MDAsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAzNzMvIn0.WDvRzyG63Ok9ZjgbACzwSDsgA1mxndtx8klY64sPg3a_oeQE-rtd0EMyYnzp4uLTDW8uU6MEzuyZeufBE5SaRXMiXH45raH468XkPvX6qbSUjV2ZV6rER36eI_gFnnnGSQzfz0vSZE1Uq8-fCxxaGpjHLrNRuDWb7o--4t4mGfI6-MOvEY46IO6pgnD9GV9BTv-SzRjuQh7BTGvKu2ITkQVqbGiwLlHN-ilu2l8HG3vB0ewePcK_mvCkhbUz8n9R6q5BHrQm2qUXGMjHE023JBb6LPr3kS_9zGKgEhWRmMkclkNiQgHtFBjMxK2jG-8v14MLaVJx4fGS4q08OqQJjg"
refresh_token:"CfDJ8EUVTnABx9VCmidQlFBiYKJ1b8N1-ggRbkY6PYVT6xwuO-xG8KL64rVld2UhXTnirKU06W2bhPPE3MU42SOji3p69aZH-VYuQQwZzXyJE3AqCh-uFQ0_XynvVQn0o15x0ozknKhk3j1N9mIvuzzZ2Lc1L5hXUL1JdhkkOQmQ42JzDSEeDFp0fwCsdAAtXdY1Cvz1Xg-WiHP8Aco4pvkFxh7vioLGcR1xmmEALHEDePj7xrnP7dzwrCe4F9bhFyTx1MW-MW5Q2kVogMwa_yxbTm4kjmO2mbt9tjpeLT_aIkE7R78AqTLw_P5pwd-nxS_rc-U9IusBpOaabdHy6bNKppxzAq-1NQr0oOf2nJMEahcKjNTMo1ip78u05Xxl4btV_f7Pf-auX7bZ1GbaqBhIrwp6Ld2tPI1E-462r-H2ufmFfW50x_5OsFcJDBR5jXolLOfo1yNStqujo0ov6AR1g5hwVaq2luYl5z-iHtgiEvbQCVNzs5anu1qeaJp3oe0GSwZ0Z2SqFu4ewWE9clFxAmH8uFvI3wwsWEXfqHiXOYFQRf4shgA9VLPktGaQd7uafuSzMUrT_xguTaaLlzmq7MM8ODPrV2qblfXTUn3BMsWLp31ZOpg6aUpR4fTDP-LLnVQVi3NTyFuQwc0dUtpYrArI4YcC97lNFTzOgf9aHXKA8_4oBaCmXQsM-39A3ynDDfV4SSmhViKUOJ7KMmb4f-Og5cckldgbgcKEXd7tSJmZOsiu5rKymKHHIU4v94_5WK3dVwDfG-pb8vg1HQP_b06lIPYNYGve80CHLMLs2V_56pwN0sRa327DY6q5rLFRZ7An8jr7Bdc94EJLYEW-gtQf29NCM5sMwlqV9xlj3ZeR-r2fBdpVsT8IjlJD5qe6KnCY6jYQAgoDagfkIy4GjBGuJmR8L4IHrsSkzJAA6nVw"
token_type:"Bearer"
和我的启动文件:
public void ConfigureServices(IServiceCollection services)
{
try
{
services.AddMvc();
services.AddEntityFrameworkSqlServer();
services.AddScoped<UserStore<AppUser, AppRole, AppDbContext, int, AppUserClaim, AppUserRole, AppUserLogin, AppUserToken, AppRoleClaim>, AppUserStore>();
services.AddScoped<UserManager<AppUser>, AppUserManager>();
services.AddScoped<RoleManager<AppRole>, AppRoleManager>();
services.AddScoped<SignInManager<AppUser>, AppSignInManager>();
services.AddScoped<RoleStore<AppRole, AppDbContext, int, AppUserRole, AppRoleClaim>, AppRoleStore>();
var connection = Configuration["ConnectionStrings"];
services.AddDbContext<AppDbContext>(options => {
options.UseSqlServer(connection);
options.UseOpenIddict<int>();
});
services
.AddIdentity<AppUser, AppRole>()
.AddUserStore<AppUserStore>()
.AddUserManager<AppUserManager>()
.AddRoleStore<AppRoleStore>()
.AddRoleManager<AppRoleManager>()
.AddSignInManager<AppSignInManager>()
.AddDefaultTokenProviders();
services.AddOpenIddict<int>()
.AddEntityFrameworkCoreStores<AppDbContext>()
.AddMvcBinders()
.EnableTokenEndpoint("/API/authorization/token")
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.UseJsonWebTokens()
.AddEphemeralSigningKey() //todo naj bi bil pravi certifikat, če odstranič to vrstico ne dela in vidiš error.
.DisableHttpsRequirement();
services.AddSingleton<DbSeeder>();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, DbSeeder dbSeeder)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
}
app.UseStaticFiles();
app.UseIdentity();
app.UseOpenIddict();
//app.UseOAuthValidation(); //this works
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
Authority = "http://localhost:50373/",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false
});
app.UseMvc();
try
{
dbSeeder.SeedAsync();
}
catch (AggregateException e)
{
throw new Exception(e.ToString());
}
}
}
网络选项卡中的开发者控制台标题:
Request URL:http://localhost:50373/api/tasks/GetLatest/
Request Method:GET
Status Code:302 Found
Remote Address:[::1]:50373
Response Headers
view source
Content-Length:0
Date:Mon, 30 Jan 2017 13:32:28 GMT
Location:http://localhost:50373/Account/Login?ReturnUrl=%2Fapi%2Ftasks%2FGetLatest%2F
Server:Kestrel
WWW-Authenticate:Bearer error="invalid_token", error_description="The audience is invalid"
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?RDpcT3BQSVNXZWJcdHJ1bmtcT3BQSVNXZWJcYXBpXHRhc2tzXEdldExhdGVzdFw=?=
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:sl-SI,sl;q=0.8,en-GB;q=0.6,en;q=0.4
authorization:Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IlJHRUVHM0FCSUJNVllHTEdQSjFSNjNRRkdHQlVKRUlHLU9ITy1QREgiLCJ0eXAiOiJKV1QifQ.eyJ1bmlxdWVfbmFtZSI6Im1hdGphei5jb2ZAb3BhbC5zaSIsIkFzcE5ldC5JZGVudGl0eS5TZWN1cml0eVN0YW1wIjoiMWVhYWE4MzktNzZlNy00NmVhLWE2NGUtYWJmNDVhNzY5YTBiIiwicm9sZSI6IkFkbWluaXN0cmF0b3JzIiwianRpIjoiM2U1NjAyMjUtMzQyNy00YmE4LTg4MzQtYzA2ZGI4NDE2NDA0IiwidXNhZ2UiOiJhY2Nlc3NfdG9rZW4iLCJzY29wZSI6WyJvcGVuaWQiLCJvZmZsaW5lX2FjY2VzcyJdLCJzdWIiOiIxIiwibmJmIjoxNDg1NzgyOTAwLCJleHAiOjE0ODU3ODQ3MDAsImlhdCI6MTQ4NTc4MjkwMCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDM3My8ifQ.IqDnWJJKRtAh0BWKcdQgg_Gm_rnarffdnq5ksqpm0m6BnslI0EZ-7CugSG0223k6_FwuU7iSCiI2Cu7O2uaUedDEOdoIwyxpaEyr6yjzT5pxw7hbMnNxmxAydEajqE3OI-C55vzK0nhv9ToS93dz_QF8MIQ5EMIJ1cGXXO9wqQQ0xLvX7o2wIlM3rYvh_OORdALBxl5byMsrtc3ZrVj-BEiYuuwrUwSU5oPlH28o_Oo030s9NGqaQNea5T3PNQAL8-qC6aIdcDLBwOYZevTGGLMxde9czk_Duc0mkp5KtsyVZ-oV3qTh-EdZxpPjttu5_5Bh-8YCDLP5AzOsyX5sbg
Cache-Control:no-cache
Connection:keep-alive
content-type:text/plain
Host:localhost:50373
Pragma:no-cache
Referer:http://localhost:50373/tasks
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
我错过了什么?
更新:
正如我在评论中所说,请务必将 access_token 保存到 id_token 本地存储:
private login(username: string, password: string)
{
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
const data = { username: username, password: password };
Object.assign(data, {
grant_type: "password",
// offline_access is required for a refresh token
scope: ['openid offline_access']
});
return this.http.post('api/authorization/token', this.encodeObjectToParams(data), options)
.map(res => res.json())
.map((tokens: AuthTokenModel) =>
{
localStorage.setItem('id_token', tokens.access_token); //!!!
return tokens;
});
}
因为Angular2-jwt默认使用 id_token 作为访问令牌。