IdentityServer4 QuckStart 受权与自定义Claims

最近在折腾IdentityServer4,为了简单,直接使用了官方给的QuickStart示例项目做为基础进行搭建。有一说一,为了保护一个API,感受花费的时间比写一个API还要多。html

本文基于ASP.NET CORE 3.1, IdentityServer4 3.1.3。代码皆为关键代码,贴全了太多了。web

好不容易跑起来了,最终的任务要落实到受权的工做上来。在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没法正常工做。json

{
    "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。参考。那么就果断给它安排。c#

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。api

//添加用户代码
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。文章写的很详细,我这就不重复了,我实际试验过,能够成功。async

可是文章末尾的注意,很重要。ide

“那么, 经过profileservice颁发的claims, 任意clients都能拿到”ui

说明这个优先级是很是高的,能够覆盖全部的行为,固然咱们能够在IProfileService的实现上对权限进行进一步的设置,不过仍是挺麻烦的。参考实现参考官方.net

做为懒人,必然不想再费劲去折腾权限的问题,那么是否有简单点的办法呢?

网上有一些问答说到了能够经过设置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就是用来干这个的,折腾了半天,不如当时仔细看看文档了。

相关文章
相关标签/搜索