【问题标题】:How do role-based authorization using identityserver4 for microservices architecture?微服务架构如何使用identityserver4进行基于角色的授权?
【发布时间】:2019-06-10 02:06:52
【问题描述】:

史前史:
我使用 IdentityServer4 为微服务开发身份验证和授权,但在授权方面存在问题。 我有两个服务:
- 使用 identityserver4 服务,这是我的身份验证服务
- 测试 MVC 项目

我成功连接了这些服务并使用了混合流,身份验证工作成功,我的角色也工作了,因为我在身份验证服务端使用自定义 ProfileService 将角色包含在 JWT 令牌中。

问题情况:
1.用户没有任何身份验证令牌。尝试在 MVC 站点上使用 [Authorize(Roles = "Admin")] 属性打开页面并重定向到 Auth 服务上的页面。 2. 用户输入登录名和密码。
3. 获得 Role=Admin 的令牌,在 Auth 服务上重定向回 MVC 站点。
4. 为用户打开的页面,因为他具有管理员角色。
5. 从身份验证服务的管理员角色中删除用户。
6. 重新加载页面并再次为该用户打开页面,这是正确的,因为在令牌中他具有角色管理员。

问题:在身份验证服务端更改角色或声明后如何实现令牌?

身份服务器配置:

public static IEnumerable<Client> GetClients()
        {
            return new Client[]
            {
                new Client
                {
                    ClientId = "Epp.Web.Mvc",
                    ClientName = "Единый Портал Потребителей",
                    AllowedGrantTypes = new List<string>{GrantType.Hybrid},
                    ClientSecrets = new List<Secret>
                    {
                        new Secret("secret".Sha256())
                    },
                    RequireConsent = false,
                    AllowAccessTokensViaBrowser = true,
                    AlwaysIncludeUserClaimsInIdToken = true,
                    AlwaysSendClientClaims = true,
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "epp",
                        "roles"
                    },
                    RedirectUris = new List<string>
                    {
                        "http://localhost:5002/signin-oidc",
                        "https://localhost:5003/signin-oidc"
                    },
                    PostLogoutRedirectUris = new List<string>{ "http://localhost:5002/signout-callback-oidc" },
                    AccessTokenLifetime = 60 * 10 
                }
            };
        }

MVC 启动:

services.AddAuthentication(options =>
                {
                    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromHours(2))
                .AddOpenIdConnect("oidc", options =>
                    {
                        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                        options.Authority = "https://localhost:5001";
                        options.ClientId = "Epp.Web.Mvc";
                        options.ResponseType = "code id_token";
                        options.ClientSecret = "secret";

                        options.GetClaimsFromUserInfoEndpoint = true;
                        options.SaveTokens = true;
                        options.RequireHttpsMetadata = false;


                        options.Scope.Add(IdentityServerConstants.StandardScopes.OpenId);
                        options.Scope.Add(IdentityServerConstants.StandardScopes.Profile);
                        options.Scope.Add("epp");
                        options.Scope.Add("roles");
                        options.TokenValidationParameters = new TokenValidationParameters
                        {
                            NameClaimType = "name",
                            RoleClaimType = "role"
                        };
                    });

【问题讨论】:

  • 不要实现你的代币,撤销它们并发行一个新的
  • 这就是为什么现在要避免基于角色的授权,并且首选基于范围的授权,例如 service1.read, service2.full_access 范围。您可以:将基于角色的授权转移到每个服务中并远离身份服务器,转换为基于范围的授权。

标签: c# asp.net-mvc authorization identityserver4 roles


【解决方案1】:

实际上,这个问题并不是专门针对 OpenId-Connect 的,也不是针对身份服务器的。关于保留一些缓存(安全)数据的时间,这是一个更普遍的问题。 JWT 在设计上是不可变的,因此您可以将其视为一种缓存。一旦缓存的项目(或 jwt)过期,我们就会得到一个新的。因此,唯一的解决方案是为您的不记名令牌设置一个合理短的到期时间,并使用刷新令牌来“实现”不记名。

我个人看不出将角色更改为给定服务的范围有什么帮助。那是有点不同的事情。我们可以或多或少地不断定义application1 可以访问service1.write 范围,从而限制All but application1' users 访问API。但是如果应用程序是通用的呢?这里没有答案。

另一个建议是使用引用令牌并在必要时使其无效。好吧,你可以做到。但是默认情况下,API 会缓存引用令牌验证的结果,所以......我们在新的地方遇到了同样的问题。

正如@VidmantasBlazevicius 建议的那样,应该将基于角色的授权转移到每个服务中,但是在服务内部你很可能有一些缓存,所以你必须再次考虑如何在必要时使其无效。

【讨论】:

  • 我会避免将此主题与缓存混为一谈,因为他的问题与缓存无关。除非他的所有微服务都遵守并尊重相同的角色上下文,否则无论您拥有何种缓存机制,此架构都注定会失败。
  • @VidmantasBlazevicius,我的意思是 jwt(在问题的上下文中)只是cache item 的一种。发出一个新的令牌在语义上与重新创建这样一个项目相同。
  • 感谢您的回答,我喜欢使用短寿命访问令牌的解决方案,但访问令牌过期时遇到奇怪的问题。我重复了官方 repo link 的示例,并设置了简短的 AccessTokenLifeTime = 60 和 IdentityTokenLifeTime = 60。然后我在我的网站上授权并等待 2 分钟。我确实重新加载了页面并使用await HttpContext.GetTokenAsync("access_token"); 检查了 Access_Token 但它已经过时了!我做错了什么?
  • 您从本地 httpContext 请求令牌,您之前通过设置 SaveTokens = true; 将其存储在其中,所以这是旧的好令牌,您是对的 :)
  • 您需要做的是使用相应的HttpClient extension method 和您保留的refresh_token。这为您提供了一对新的访问和刷新令牌
【解决方案2】:

我看到以下两种方法,

  1. 当您的角色/权限发生更改时,您可以用户引用令牌流并使令牌无效。查看链接http://docs.identityserver.io/en/latest/topics/reference_tokens.html

  2. 或者如@Vidmantas Blazevicius 所述,将基于角色的授权作业转移到相应的服务,而不是在身份服务器上中继。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-12
    • 1970-01-01
    • 2018-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 2016-07-20
    相关资源
    最近更新 更多