最近在折腾IdentityServer4,为了简单,直接使用了官方给的QuickStart示例项目作为基础进行搭建。有一说一,为了保护一个API,感觉花费的时间比写一个API还要多。
本文基于ASP.NET CORE 3.1,IdentityServer4 3.1.3。代码皆为关键代码,贴全了太多了。
好不容易跑起来了,最终的任务要落实到授权的工作上来。在API中使用Authorize
用来限制用户的访问。
[Route("api/[controller]")]
[Authorize(Roles = "Administrator")]
[ApiController]
public class UserInfoController : ControllerBase
{
/// <summary>
/// 无参GET请求
/// </summary>
/// <returns></returns>
[HttpGet()]
[ProducesResponseType(typeof(ReturnData<IEnumerable<UserInfo>>),Status200OK)]
public async Task<ActionResult> Get()
{
var info = new Info<UserInfo>();
return Ok(new ReturnData<IEnumerable<UserInfo>>(await info.Get()));
}
然而在使用的时候,虽然正确取得授权,但是却无法正常访问API,一直提示401没有授权错误。仔细检查,发现IdentityServer4返回的内容并没有返回role的JwtClaimTypes
,没有它,Authorize
无法正常工作。
{
"nbf": 1587301921,"exp": 1587305521,"iss": "http://localhost:5000","aud": "MonitoringSystemApi","client_id": "webClient","sub": "c6c18d4d-c28e-4de5-86dd-779121216204","auth_time": 1587301921,"idp": "local","scope": [
"roles","MonitoringSystemApi","offline_access"
],"amr": [
"pwd"
]
}
实现
查看Config.cs,IdentityServer4默认只返回两种IdentityResource:openid和profile。按照官方的说法,这个东西定义的内容会返回到用户的token。参考。那么就果断给它安排。
public static IEnumerable<IdentityResource> Ids =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),new IdentityResources.Profile(),new IdentityResource ("roles",new List<string> { JwtClaimTypes.Role }){ Required = true}
};
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "webClient",ClientSecrets = { new Secret("secret".Sha256()) },AllowOfflineAccess = true,AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,// scopes that client has access to
AllowedScopes = {
"roles","MonitoringSystemApi" }
},
执行之前,需要确保数据库中的用户数据,已经包含role的Claim。
//添加用户代码
bob = new ApplicationUser
{
UserName = "bob"
};
var result = userMgr.CreateAsync(bob,"Pass123$").Result;
if (!result.Succeeded)
{
throw new Exception(result.Errors.First().Description);
}
result = userMgr.AddClaimsAsync(bob,new Claim[]{
new Claim(JwtClaimTypes.Role,"Administrator"),new Claim(JwtClaimTypes.Name,"Bob Smith"),
运行程序,返回值依旧没有任何变化,很挫败,只能继续折腾。
有研究通过实现IProfileService
达到自定义Cliams。文章写的很详细,我这就不重复了,我实际试验过,可以成功。
但是文章末尾的注意,很重要。
“那么,通过profileservice颁发的claims,任意clients都能拿到”
说明这个优先级是非常高的,可以覆盖所有的行为,当然我们可以在IProfileService
的实现上对权限进行进一步的设置,不过还是挺麻烦的。参考实现,参考官方
作为懒人,必然不想再费劲去折腾权限的问题,那么是否有简单点的办法呢?
网上有一些问答说到了可以通过设置Scopes来达到目的。不过过于久远,IdentityServer4已经没有这个独立的类了,说是已经被ApiResource
取代了。
直觉上这个东西应该是指示要保护的API的相关内容的,好像和这个没啥关系,不过也只能死马当活马医了。修改config.cs,最终如下内容:
public static IEnumerable<ApiResource> Apis =>
new List<ApiResource>
{
new ApiResource("pls",new[]{ "role"}),};
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "webClient",// scopes that client has access to
AllowedScopes = {
"pls"
}
},
返回结果如下:
{
"nbf": 1587301799,"exp": 1587305399,"aud": "pls","auth_time": 1587301799,"role": "Administrator","scope": [
"pls","amr": [
"pwd"
]
}
终于看见心心念念的自定义Claim(role),可以去访问API了。
注意,在Client中也有个Claims,添加了role并且设置
AlwaysSendClientClaims
和AlwaysIncludeUserClaimsInIdToken
之后,会在token中添加client_roie
字段,这个是没办法用与授权的,可以理解为IdentityServer4直接指定了Client角色,并不是Identity中的角色概念。
后记
回过头来仔细看官方的文档,ApiResource中的UserClaims就是用来干这个的,折腾了半天,不如当时仔细看看文档了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。