始终在身份 2 中使用不记名令牌获得 "Authorization has been denied for this request."?
Always get "Authorization has been denied for this request." using Bearer tokens in Identity 2?
使用邮递员我可以请求一个令牌,这里是:
{
"access_token": "N1FL606bmDkZyLplpkLAihaviMQhB042z-rhY262M_W5nSWIv8fDOQiYkEn6GCuDnrxpdOWBS7lpxlBazHYlwnP1RvpDFED1i_ml89QNspyGOWB6TcMkT1MmfUAZ617k9MNvl5UJh2jKzUwvDDeXMURG9tEtmE3UX2L2D-1VA9kqYOzOB1UYbpMAfdTi84jsbR0lhLkNkReQ5fqg4B3IFbbWNGWu5ONb1uuf00ixL-BIMqSvEaNn58_zCyAVFWVzcH2tayYTGT5p_AItKfYiWaYHKC0pDoZ_OBdlpB7Odc7ScwjwFM5vtpBZE81rpk8yjXnrTEk_j9n0eiloJnpWwA",
"token_type": "bearer",
"expires_in": 899,
"refresh_token": "60da311d10f043b892c703c7fb7ab061",
"as:client_id": "Erp",
"userName": "bbauer",
".issued": "Tue, 30 Jun 2015 17:56:10 GMT",
".expires": "Tue, 30 Jun 2015 18:11:10 GMT"
}
我还可以像这样从不受保护的资源中获取信息:
http://localhost:60689/api/Accounts/User/bbauer
{
"url": "http://localhost:60689/api/accounts/user/31",
"id": 31,
"userName": "bbauer",
"fullName": "Brian Bauer",
"email": null,
"emailConfirmed": false,
"roles": [
"Administrator"
],
"claims": []
}
从中我看到用户处于 "Administrator" 角色。当我尝试获取受保护的资源时,我总是会返回:"Authorization has been denied for this request."
控制器中的方法如下:
[Authorize(Roles = "Administrator")]
[Route("user/{id:int}", Name = "GetUserById")]
public async Task<IHttpActionResult> GetUser(int id)
{
var user = await AppUserManager.FindByIdAsync(id);
if (user != null)
{
return Ok(TheModelFactory.Create(user));
}
return NotFound();
}
这是我在邮递员中的设置:
http://localhost:60689/api/Accounts/User/31
Content-Type: application/json
接受: application/json
授权:承载N1FL606bmDkZyLplpkLAihaviMQhB042z-rhY262M_W5nSWIv8fDOQiYkEn6GCuDnrxpdOWBS7lpxlBazHYlwnP1RvpDFED1i_ml89QNspyGOWB6TcMkT1MmfUAZ617k9MNvl5UJh2jKzUwvDDeXMURG9tEtmE3UX2L2D-1VA9kqYOzOB1UYbpMAfdTi84jsbR0lhLkNkReQ5fqg4B3IFbbWNGWu5ONb1uuf00ixL-BIMqSvEaNn58_zCyAVFWVzcH2tayYTGT5p_AItKfYiWaYHKC0pDoZ_OBdlpB7Odc7ScwjwFM5vtpBZE81rpk8yjXnrTEk_j9n0eiloJnpWwA
我可以使用 fiddler 来验证正在发送的授权 header。另一件需要注意的事情是,当我传递 access_token 以获取不受保护的 /user/username 资源时,我可以中断代码并使用这些设置查看 ClaimsPrincipal:
AuthenticationType: Bearer
IsAuthenticated: 真
姓名: 鲍尔
但是,如果我测试 User.IsInRole("Administrator") 它总是错误的。为什么是假的? AspNetUserRole table 有条目,当我获取用户时,我看到他的一个角色 "Administrator"...我在上帝的绿色地球上错过了什么?
这是我的 Startup class 如果有帮助的话:
public class Startup
{
public static OAuthAuthorizationServerOptions OAuthServerOptions { get; private set; }
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(httpConfig);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(httpConfig);
}
public void ConfigureOAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(15),
Provider = new SimpleAuthorizationServerProvider(PublicClientId),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
原来我需要在我的 SimpleAuthorizationServerProvider 的 GrantResourceOwnerCredentials 方法中向我的 ClaimsIdentity 添加角色。这是代码(见评论部分):
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin") ?? "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
//this loop is where the roles are added as claims
foreach (var role in userManager.GetRoles(user.Id))
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", context.ClientId ?? string.Empty
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
使用邮递员我可以请求一个令牌,这里是:
{
"access_token": "N1FL606bmDkZyLplpkLAihaviMQhB042z-rhY262M_W5nSWIv8fDOQiYkEn6GCuDnrxpdOWBS7lpxlBazHYlwnP1RvpDFED1i_ml89QNspyGOWB6TcMkT1MmfUAZ617k9MNvl5UJh2jKzUwvDDeXMURG9tEtmE3UX2L2D-1VA9kqYOzOB1UYbpMAfdTi84jsbR0lhLkNkReQ5fqg4B3IFbbWNGWu5ONb1uuf00ixL-BIMqSvEaNn58_zCyAVFWVzcH2tayYTGT5p_AItKfYiWaYHKC0pDoZ_OBdlpB7Odc7ScwjwFM5vtpBZE81rpk8yjXnrTEk_j9n0eiloJnpWwA",
"token_type": "bearer",
"expires_in": 899,
"refresh_token": "60da311d10f043b892c703c7fb7ab061",
"as:client_id": "Erp",
"userName": "bbauer",
".issued": "Tue, 30 Jun 2015 17:56:10 GMT",
".expires": "Tue, 30 Jun 2015 18:11:10 GMT"
}
我还可以像这样从不受保护的资源中获取信息: http://localhost:60689/api/Accounts/User/bbauer
{
"url": "http://localhost:60689/api/accounts/user/31",
"id": 31,
"userName": "bbauer",
"fullName": "Brian Bauer",
"email": null,
"emailConfirmed": false,
"roles": [
"Administrator"
],
"claims": []
}
从中我看到用户处于 "Administrator" 角色。当我尝试获取受保护的资源时,我总是会返回:"Authorization has been denied for this request."
控制器中的方法如下:
[Authorize(Roles = "Administrator")]
[Route("user/{id:int}", Name = "GetUserById")]
public async Task<IHttpActionResult> GetUser(int id)
{
var user = await AppUserManager.FindByIdAsync(id);
if (user != null)
{
return Ok(TheModelFactory.Create(user));
}
return NotFound();
}
这是我在邮递员中的设置:
http://localhost:60689/api/Accounts/User/31
Content-Type: application/json
接受: application/json
授权:承载N1FL606bmDkZyLplpkLAihaviMQhB042z-rhY262M_W5nSWIv8fDOQiYkEn6GCuDnrxpdOWBS7lpxlBazHYlwnP1RvpDFED1i_ml89QNspyGOWB6TcMkT1MmfUAZ617k9MNvl5UJh2jKzUwvDDeXMURG9tEtmE3UX2L2D-1VA9kqYOzOB1UYbpMAfdTi84jsbR0lhLkNkReQ5fqg4B3IFbbWNGWu5ONb1uuf00ixL-BIMqSvEaNn58_zCyAVFWVzcH2tayYTGT5p_AItKfYiWaYHKC0pDoZ_OBdlpB7Odc7ScwjwFM5vtpBZE81rpk8yjXnrTEk_j9n0eiloJnpWwA
我可以使用 fiddler 来验证正在发送的授权 header。另一件需要注意的事情是,当我传递 access_token 以获取不受保护的 /user/username 资源时,我可以中断代码并使用这些设置查看 ClaimsPrincipal:
AuthenticationType: Bearer
IsAuthenticated: 真
姓名: 鲍尔
但是,如果我测试 User.IsInRole("Administrator") 它总是错误的。为什么是假的? AspNetUserRole table 有条目,当我获取用户时,我看到他的一个角色 "Administrator"...我在上帝的绿色地球上错过了什么?
这是我的 Startup class 如果有帮助的话:
public class Startup
{
public static OAuthAuthorizationServerOptions OAuthServerOptions { get; private set; }
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(httpConfig);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(httpConfig);
}
public void ConfigureOAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(15),
Provider = new SimpleAuthorizationServerProvider(PublicClientId),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
原来我需要在我的 SimpleAuthorizationServerProvider 的 GrantResourceOwnerCredentials 方法中向我的 ClaimsIdentity 添加角色。这是代码(见评论部分):
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin") ?? "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
//this loop is where the roles are added as claims
foreach (var role in userManager.GetRoles(user.Id))
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", context.ClientId ?? string.Empty
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}