Aps .net IdentityServer4授权

Aps .net IdentityServer4 authorize

我正在使用带有 asp .net 身份的 IdentityServer4 作为身份验证点。我的 APIs/WebApps 调用身份服务器以获取访问令牌。

现在,如何在我的 api/app 控制器中的某些操作或内部操作之前授权使用?

我可以将角色添加到访问令牌,然后在控制器中(在网络 api/web 应用程序中)使用 AuthorizeAttribute 并检查用户是否 IsInRole。

但这意味着如果我要更改用户角色,他将在注销-登录后看到它(因为角色是访问令牌的一部分)或者令牌必须过期。

每次我需要授权他执行某些操作(尤其是像 modify/delete 某些数据这样的操作)时,我想询问身份服务器有关用户角色的信息。

问题如何? 或者我需要寻找什么?

所以这里有一些可能的解决方案:

  • 调用 OIDC UserInfo 端点以获取每个请求的更新用户声明
  • 缩短 cookie 生命周期以更频繁地自动刷新用户信息
  • 在 IdentityServer 上实现一个自定义端点,以便 post 将更改信息配置到订阅的客户端列表(例如您的 Web 应用程序)。
  • 让 IdentityServer 在用户配置文件数据更改时强制单点注销

就实施难度而言,降低 cookie 生命周期是最简单的(只需更改 cookie 过期时间),但不能保证声明是最新的,并且对用户可见(频繁重定向到 IdentityServer ,尽管如果访问令牌生命周期仍然有效则不需要登录)

让 webapp 在每个请求上调用 UserInfo 端点是下一个最简单的方法(参见下面的示例),但对性能的影响最差。每个请求都会产生到 IdentityServer 的往返行程。

端点/订阅者模型的性能开销最低。 UserInfo 对 IdentityServer 的请求只会在用户配置文件信息实际更改时发生。实现起来会有点复杂:

  1. 在您的 IdentityServer 项目中,您需要修改配置文件数据的更改,并且 post 向您的 webapp 发送一条 http 消息。该消息可以只包含修改后的用户的用户 ID。此消息需要以某种方式进行身份验证,以防止恶意用户使合法用户会话无效。您可以为此包含一个 ClientCredentials 不记名令牌。
  2. 您的网络应用程序需要接收和验证消息。它需要将更改后的用户 ID 存储在 OnValidatePrincipal 委托可以访问的某个地方(最有可能通过 DI 容器中的服务)
  3. Cookie OnValidatePrincipal 委托随后会注入此本地服务,以在验证主体之前检查用户信息是否已更改

代码示例

在每次调用时从端点获取更新的 UserInfo

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "NameOfYourCookieAuthSchemeHere",
    Events = new CookieAuthenticationEvents()
    {
        OnValidatePrincipal = async context =>
        {
            // Get updated UserInfo from IdentityServer
            var accessToken = context.Principal.Claims.FirstOrDefault(c => c.Type == "access_token").Value;
            var userInfoClient = new UserInfoClient("https://{IdentityServerUrlGoesHere}");
            var userInfoResponse = await userInfoClient.GetAsync(accessToken);

            // Invalidate Principal if Error Response
            if (userInfoResponse.IsError)
            {
                context.RejectPrincipal();
                await context.HttpContext.Authentication.SignOutAsync("NameOfYourCookieAuthSchemeHere");
            }
            else
            {
                // Check if claims changed
                var claimsChanged = userInfoResponse.Claims.Except(context.Principal.Claims).Any();
                if (claimsChanged)
                {
                    // Update claims and replace principal
                    var newIdentity = context.Principal.Identity as ClaimsIdentity;
                    newIdentity.AddClaims(userInfoResponse.Claims);
                    var updatedPrincipal = new ClaimsPrincipal();
                    context.ReplacePrincipal(updatedPrincipal);
                    context.ShouldRenew = true;
                }
            }
        }
    }
});

更新来自 IdentityServer 的已订阅更改消息。此示例假设您已经创建了一个服务(例如 IUserChangedService),该服务存储端点从 IdentityServer 接收到的 userId。我没有 webapp 的接收端点或服务的样本。

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "NameOfYourCookieAuthSchemeHere",
    Events = new CookieAuthenticationEvents()
    {
        OnValidatePrincipal = async context =>
        {
            // Get User ID
            var userId = context.Principal.Claims.FirstOrDefault(c => c.Type == "UserIdClaimTypeHere");

            var userChangedService = context.HttpContext.RequestServices.GetRequiredService<IUserChangedService>();
            var userChanged = await userChangedService.HasUserChanged(userId);

            if (userChanged)
            {
                // Make call to UserInfoEndpoint and update ClaimsPrincipal here. See example above for details
            }
        }
    }
});

asp.net 核心文档也有这方面的示例,但使用本地数据库除外。连接到 OnValidatePrincipal 方法的方法是相同的: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie#reacting-to-back-end-changes

希望对您有所帮助!