• IdentityServer4 QuickStart 授权与自定义Claims


    最近在折腾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",
        ClientSecrets = { new Secret("secret".Sha256()) },
        AllowOfflineAccess = true,
        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
        // scopes that client has access to
        AllowedScopes = {
            "pls"
            }
    },
    

    返回结果如下:

    {
        "nbf": 1587301799,
        "exp": 1587305399,
        "iss": "http://localhost:5000",
        "aud": "pls",
        "client_id": "webClient",
        "sub": "c6c18d4d-c28e-4de5-86dd-779121216204",
        "auth_time": 1587301799,
        "idp": "local",
        "role": "Administrator",
        "scope": [
            "pls",
            "offline_access"
        ],
        "amr": [
            "pwd"
        ]
    }
    

    终于看见心心念念的自定义Claim(role),可以去访问API了。

    注意,在Client中也有个Claims,添加了role并且设置AlwaysSendClientClaimsAlwaysIncludeUserClaimsInIdToken之后,会在token中添加client_roie字段,这个是没办法用与授权的,可以理解为IdentityServer4直接指定了Client角色,并不是Identity中的角色概念。

    后记

    回过头来仔细看官方的文档,ApiResource中的UserClaims就是用来干这个的,折腾了半天,不如当时仔细看看文档了。

  • 相关阅读:
    cygwin 下配置ssh
    使用MarsEdit写博客
    bash no job control in this shell
    安装devtoolset-2:因由安装gcc 4.8而引起
    AFNetworking Property with 'retain (or strong)' attribute must be of object type
    从xib 创建 collectionViewCell
    CocoaPods 安装
    个人理解的 Https 通信流程
    cellforrowatindexpath 不执行 的原因
    do{} while(0) 的意义和用法
  • 原文地址:https://www.cnblogs.com/podolski/p/12734603.html
Copyright © 2020-2023  润新知