【问题标题】:Role based authorization with IdentityServer4IdentityServer4 基于角色的授权
【发布时间】:2017-04-12 04:13:09
【问题描述】:

我正在尝试使用 IdentityServer4 实现“基于角色的授权”,以根据用户角色授予对我的 API 的访问权限。

例如,我想为用户提供两个角色,即 FreeUser 和 PaidUser,并希望使用 [Authorize(Roles = "FreeUser"))] 通过授权属性授予对 API 的访问权限,请帮助我如何我做到了。

我有以下解决方案结构:

  1. 身份服务器
  2. WebApi
  3. Javascript 客户端

我已按如下方式注册了我的 Javascript 客户端:

 new Client
            {
                ClientId = "js",
                ClientName = "javascript client",
                AllowedGrantTypes = GrantTypes.Implicit,
                AllowAccessTokensViaBrowser= true,
                RedirectUris = {"http://localhost:5004/callback.html"},
                PostLogoutRedirectUris = {"http://localhost:5004/index.html"},
                AllowedCorsOrigins = {"http://localhost:5004"},

                AllowedScopes =
                {
                    StandardScopes.OpenId.Name,
                    StandardScopes.Profile.Name,
                    "api1",
                    "role",
                    StandardScopes.AllClaims.Name
                }
            }

作用域

 return new List<Scope>
        {
            StandardScopes.OpenId,
            StandardScopes.Profile,

            new Scope
            {
                Name = "api1",
                Description = "My API"
            },
           new Scope
           {
               Enabled = true,
               Name  = "role",
               DisplayName = "Role(s)",
               Description = "roles of user",
               Type = ScopeType.Identity,
               Claims = new List<ScopeClaim>
               {
                   new ScopeClaim("role",false)
               }
           },
           StandardScopes.AllClaims
        };

用户

 return new List<InMemoryUser>
        {
            new InMemoryUser
            {
                Subject = "1",
                Username = "alice",
                Password = "password",

                Claims = new List<Claim>
                {
                    new Claim("name", "Alice"),
                    new Claim("website", "https://alice.com"),
                    new Claim("role","FreeUser")
                }
            },
            new InMemoryUser
            {
                Subject = "2",
                Username = "bob",
                Password = "password",

                Claims = new List<Claim>
                {
                    new Claim("name", "Bob"),
                    new Claim("website", "https://bob.com"),
                    new Claim("role","PaidUser")
                }
            }
        };

WebApi Startup.cs

  public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();


        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        app.UseCors("default");
        app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
        {
            Authority = "http://localhost:5000",
            ScopeName = "api1",
            //  AdditionalScopes = new List<string> { "openid","profile", "role" },
            RequireHttpsMetadata = false
        });

        app.UseMvc();
    }

Web API 控制器

namespace Api.Controllers
{
 [Route("[controller]")]

public class IdentityController : ControllerBase
{
    [HttpGet]
    [Authorize(Roles = "PaidUser")]
    public IActionResult Get()
    {
        return new JsonResult(from c in User.Claims select new { c.Type,    c.Value });
    }

    [Authorize(Roles = "FreeUser")]
    [HttpGet]
    [Route("getfree")]
    public IActionResult GetFreeUser()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}
}

Javascript 客户端 app.js 在这里,我尝试通过 IdentityServer 登录用户并发出 API 请求。

var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
    log("User logged in", user.profile);
} else {
    log("User is not logged in.");
}
});

function login() {
  mgr.signinRedirect();
 }

function api() {
mgr.getUser().then(function (user) {
    var url = "http://localhost:5001/identity/getfree";

    var xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = function () {
        log(xhr.status, JSON.parse(xhr.responseText));
    };

    xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
    xhr.send();
  });
 }

 function logout() {
   mgr.signoutRedirect();
 }

登录流程正常,我可以成功登录,并且可以在访问令牌中接收角色。

当我通过单击按钮 (Call Api) 向 API 发出请求时,我收到以下错误。

【问题讨论】:

  • 这是一个较老的问题,但您是同时使用IdentityServer4 和Asp.Net Core Identity 还是只使用IdentityServer4?

标签: asp.net-mvc asp.net-core identityserver4


【解决方案1】:

将new Claim("role","FreeUser") 更改为new Claim(ClaimTypes.Role, "FreeUser")

或者创建一个这样的策略:

services.AddAuthorization(options =>
{
    options.AddPolicy("FreeUser", policy => policy.RequireClaim("role", "FreeUser"));
});

并使用它:

[Authorize(Policy = "FreeUser")]

【讨论】:

  • 感谢您的回复,这两种方法我都试过了,但结果是一样的,如果我在发出 API 请求时添加策略,它会向 API 发出两个请求,第一个说 204 [No Content] 状态代码,而第二个状态代码因 403 失败。如果发生索赔,结果仍然是相同的 403 ..我有什么遗漏吗?..
【解决方案2】:

鉴于您没有为 javascript 客户端提供配置对象,我假设您的范围配置如下。

scope:"openid profile api1 role"

我认为您的问题的主要原因是角色声明未包含在您的访问令牌中。

将角色声明添加到 api1 范围如下,以将其包含在访问令牌中。

             new Scope
                {
                    Name = "api1",
                    DisplayName = "API1 access",
                    Description = "My API",
                    Type = ScopeType.Resource,
                    IncludeAllClaimsForUser = true,
                    Claims = new List<ScopeClaim>
                    {
                        new ScopeClaim(ClaimTypes.Name),
                        new ScopeClaim(ClaimTypes.Role)
                    }
                }

您可以在此处阅读我的答案以帮助调试问题。 implementing roles in identity server 4 with asp.net identity

完整的工作解决方案在这里。 https://github.com/weliwita/IdentityServer4.Samples/tree/40844310

【讨论】:

  • 太棒了..它现在可以正常工作了..我不敢相信我怎么忘记将它添加到 Scopes 中.. 无论如何谢谢..
  • @muhammadwaqas 我认为你应该接受答案
  • 2018 年的今天,IdentityServer4.AspNetIdentity Version="2.1.0" Scope 类被 ApiResource 取代,所以我尝试做一些适应性,但我做不到。有人为2.1.0 版本实现它吗?
【解决方案3】:

我在这篇文章中写了一个示例

Identity Server 4: adding claims to access token

我已经测试了角色和声明,我也可以在客户端 Web 应用和 API 应用中使用 [Authorize(Role="SuperAdmin, Admin")]。

【讨论】:

    【解决方案4】:

    我的角色在 .NET 5 中是这样工作的:

    在Startup.cs 中的services.AddAuthentication 之前添加JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();。

    我也加了

    services.AddScoped<IProfileService, ProfileService>();
    

    和ProfileService.cs 看起来像这样将角色映射到声明:

    public sealed class ProfileService : IProfileService
    {
        private readonly IUserClaimsPrincipalFactory<ApplicationUser> _userClaimsPrincipalFactory;
        private readonly UserManager<ApplicationUser> _userMgr;
        private readonly RoleManager<IdentityRole> _roleMgr;
    
        public ProfileService(
            UserManager<ApplicationUser> userMgr,
            RoleManager<IdentityRole> roleMgr,
            IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory)
        {
            _userMgr = userMgr;
            _roleMgr = roleMgr;
            _userClaimsPrincipalFactory = userClaimsPrincipalFactory;
        }
    
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            string sub = context.Subject.GetSubjectId();
            ApplicationUser user = await _userMgr.FindByIdAsync(sub);
            ClaimsPrincipal userClaims = await _userClaimsPrincipalFactory.CreateAsync(user);
    
            List<Claim> claims = userClaims.Claims.ToList();
            claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
    
            if (_userMgr.SupportsUserRole)
            {
                IList<string> roles = await _userMgr.GetRolesAsync(user);
                foreach (var roleName in roles)
                {
                    claims.Add(new Claim(JwtClaimTypes.Role, roleName));
                    if (_roleMgr.SupportsRoleClaims)
                    {
                        IdentityRole role = await _roleMgr.FindByNameAsync(roleName);
                        if (role != null)
                        {
                            claims.AddRange(await _roleMgr.GetClaimsAsync(role));
                        }
                    }
                }
            }
    
            context.IssuedClaims = claims;
        }
    
        public async Task IsActiveAsync(IsActiveContext context)
        {
            string sub = context.Subject.GetSubjectId();
            ApplicationUser user = await _userMgr.FindByIdAsync(sub);
            context.IsActive = user != null;
        }
    }
    

    来源:

    https://ffimnsr.medium.com/adding-identity-roles-to-identity-server-4-in-net-core-3-1-d42b64ff6675

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-27
      • 1970-01-01
      • 2022-01-01
      • 2023-03-21
      相关资源
      最近更新 更多