zoukankan      html  css  js  c++  java
  • Blazor Server访问Identity Server 4单点登录2-集成Asp.Net角色

    采用内存用户集合的模板创建的项目,是不能用于生产环境的,毕竟用户信息只存在于内存。Id4有一个模板是可以采用Asp.Net内置的用户数据库的,直接创建这个模板的项目即可。

    创建AspNet用户数据库的Identity Server 4项目

    在控制台进入解决方案目录,创建is4aspid模板项目。

    D:Softwaregiteelzid4BlzId4Web>dotnet new is4aspid -n AspNetId4Web

    已成功创建模板“IdentityServer4 with ASP.NET Core Identity”

    正在处理创建后操作...

    模板配置为运行以下操作:

    说明:

    手动说明: Seeds the initial user database

    实际命令: dotnet run /seed

    是否要运行此操作 [Y()|N()]?

    y

    正在运行命令:“dotnet run /seed”...

    命令已成功。

    参考前文《Blazor Server访问Identity Server 4单点登录》

    https://www.cnblogs.com/sunnytrudeau/p/15145388.html

    Config.cs新增2个客户端定义

                    new Client()
                    {
                        ClientId="BlazorServer1",
                        ClientName = "BlazorServer1",
                        ClientSecrets=new []{new Secret("BlazorServer1.Secret".Sha256())},
    
                        AllowedGrantTypes = GrantTypes.Code,
                        
                        AllowedCorsOrigins = { "https://localhost:5101" },
                        RedirectUris = { "https://localhost:5101/signin-oidc" },
                        PostLogoutRedirectUris = { "https://localhost:5101/signout-callback-oidc" },
    
                        AllowedScopes = { "openid", "profile", "scope1" }
                    },
    
                    new Client()
                    {
                        ClientId="BlazorServer2",
                        ClientName = "BlazorServer2",
                        ClientSecrets=new []{new Secret("BlazorServer2.Secret".Sha256())},
    
                        AllowedGrantTypes = GrantTypes.Code,
    
                        AllowedCorsOrigins = { "https://localhost:5201" },
                        RedirectUris = { "https://localhost:5201/signin-oidc" },
                        PostLogoutRedirectUris = { "https://localhost:5201/signout-callback-oidc" },
    
                        AllowedScopes = { "openid", "profile", "scope1" }
                    },
    

      

    创建Blazor Server项目

    参考前文《Blazor Server访问Identity Server 4单点登录》,创见2Blazor Server客户端项目。

    然后运行3个项目,测试验证id4单点登录。

    增加角色

    Asp.Net项目最常见的授权策略就是基于角色,默认创建的Identity Server 4项目是没有角色的,得手工加上。这个还相当麻烦。

    首先要给用户增加角色,在SeedData增加函数AddRole

    /// <summary>
            /// 给测试用户增加角色
            /// </summary>
            /// <param name="serviceProvider"></param>
            public static void AddRole(IServiceProvider serviceProvider)
            {
                using var scope = serviceProvider.CreateScope();
                using var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
                using var userMgr = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
    
                bool roleExists = roleManager.RoleExistsAsync("Admin").Result;
                if (!roleExists)
                {
                    var result = roleManager.CreateAsync(new IdentityRole("Admin"));
                }
    
                roleExists = roleManager.RoleExistsAsync("Guest").Result;
                if (!roleExists)
                {
                    var result = roleManager.CreateAsync(new IdentityRole("Guest"));
                }
    
                var alice = userMgr.FindByNameAsync("alice").Result;
                if (alice != null)
                {
                    var roles = userMgr.GetRolesAsync(alice).Result;
                    if (!roles.Contains("Admin"))
                    {
                        var result = userMgr.AddToRoleAsync(alice, "Admin");
                    }
                    if (!roles.Contains("Guest"))
                    {
                        var result = userMgr.AddToRoleAsync(alice, "Guest");
                    }
                }
    
                var bob = userMgr.FindByNameAsync("bob").Result;
                if (bob != null)
                {
                    var roles = userMgr.GetRolesAsync(bob).Result;
                    if (!roles.Contains("Guest"))
                    {
                        var result = userMgr.AddToRoleAsync(bob, "Guest");
                    }
                }
            }
    

      

    id4项目初始化阶段检查执行,增加角色。

    SeedData.AddRole(host.Services);

    id4项目config增加role资源

    public static IEnumerable<IdentityResource> IdentityResources =>
                new IdentityResource[]
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile(),
                    new IdentityResource("role", new string[]{JwtClaimTypes.Role })
                };
    

      

    客户端配置AllowedScopes也增加role

    new Client()
                    {
                        ClientId="BlazorServer1",
                        ClientName = "BlazorServer1",
                        ClientSecrets=new []{new Secret("BlazorServer1.Secret".Sha256())},
    
                        AllowedGrantTypes = GrantTypes.Code,
    
                        AllowedCorsOrigins = { "https://localhost:5101" },
                        RedirectUris = { "https://localhost:5101/signin-oidc" },
                        PostLogoutRedirectUris = { "https://localhost:5101/signout-callback-oidc" },
    
                        //效果等同客户端项目配置options.GetClaimsFromUserInfoEndpoint = true
                        //AlwaysIncludeUserClaimsInIdToken = true,
    
                        AllowedScopes = { "openid", "profile", "scope1", "role", }
                    },
    

      

    Blazor Server项目增加获取rolescope,并且在id4用户信息端点拿到role之后要手工解析,因为id4返回的role可能是单个字符串,也可能是字符串数组,Blazor Server不认识。

    //默认采用cookie认证方案,添加oidc认证方案
                services.AddAuthentication(options =>
                    {
                        options.DefaultScheme = "cookies";
                        options.DefaultChallengeScheme = "oidc";
                    })
                    //配置cookie认证
                    .AddCookie("cookies")
                    .AddOpenIdConnect("oidc", options =>
                    {
                        //id4服务的地址
                        options.Authority = "https://localhost:5001";
    
                        //id4配置的ClientId以及ClientSecrets
                        options.ClientId = "BlazorServer1";
                        options.ClientSecret = "BlazorServer1.Secret";
    
                        //认证模式
                        options.ResponseType = "code";
    
                        //保存token到本地
                        options.SaveTokens = true;
    
                        //很重要,指定从Identity Server的UserInfo地址来取Claim
                        //效果等同id4配置AlwaysIncludeUserClaimsInIdToken = true
                        options.GetClaimsFromUserInfoEndpoint = true;
    
                        //指定要取哪些资料(除Profile之外,Profile是默认包含的)
                        options.Scope.Add("scope1");
                        options.Scope.Add("role");
    
                        //这里是个ClaimType的转换,Identity Server的ClaimType和Blazor中间件使用的名称有区别,需要统一。
                        //User.Identity.Name=JwtClaimTypes.Name
                        options.TokenValidationParameters.NameClaimType = "name";
                        options.TokenValidationParameters.RoleClaimType = "role";
    
                        options.Events.OnUserInformationReceived = (context) =>
                        {
                            //id4返回的角色是字符串数组或者字符串,blazor server的角色是字符串,需要转换,不然无法获取到角色
                            ClaimsIdentity claimsId = context.Principal.Identity as ClaimsIdentity;
    
                            var roleElement = context.User.RootElement.GetProperty(JwtClaimTypes.Role);
                            if (roleElement.ValueKind == System.Text.Json.JsonValueKind.Array)
                            {
                                var roles = roleElement.EnumerateArray().Select(e => e.ToString());
                                claimsId.AddClaims(roles.Select(r => new Claim(JwtClaimTypes.Role, r)));
                            }
                            else
                            {
                                claimsId.AddClaim(new Claim(JwtClaimTypes.Role, roleElement.ToString()));
                            }
    
                            return Task.CompletedTask;
                        };
                    });
    

      

    测试获取角色

    同时运行3个项目,在Blazor Server项目主页用alice登录,可以看到正确获取到了role

    id4控制台信息可以看到,Blazor Server客户端请求用户信息时,返回了role字段。

    [23:37:38 Debug] IdentityServer4.Validation.TokenValidator

    Token validation success

    {"ClientId": null, "ClientName": null, "ValidateLifetime": true, "AccessTokenType": "Jwt", "ExpectedScope": "openid", "TokenHandle": null, "JwtId": "3223AFCB3DB832A92A69A04470AE144F", "Claims": {"nbf": 1630856258, "exp": 1630859858, "iss": "https://localhost:5001", "aud": "https://localhost:5001/resources", "client_id": "BlazorServer1", "sub": "845589e3-2f6d-4175-a5c1-92cc589ead01", "auth_time": 1630856258, "idp": "local", "jti": "3223AFCB3DB832A92A69A04470AE144F", "sid": "7C09FC1C3A8B1D645FA6ECE46A690580", "iat": 1630856258, "scope": ["openid", "profile", "scope1", "role"], "amr": "pwd"}, "$type": "TokenValidationLog"}

    [23:37:38 Debug] IdentityServer4.ResponseHandling.UserInfoResponseGenerator

    Creating userinfo response

    [23:37:38 Debug] IdentityServer4.ResponseHandling.UserInfoResponseGenerator

    Scopes in access token: openid profile scope1 role

    [23:37:38 Debug] IdentityServer4.ResponseHandling.UserInfoResponseGenerator

    Requested claim types: sub name family_name given_name middle_name nickname preferred_username profile picture website gender birthdate zoneinfo locale updated_at role

    [23:37:38 Information] IdentityServer4.ResponseHandling.UserInfoResponseGenerator

    Profile service returned the following claim types: sub name given_name family_name website role role preferred_username

    [23:37:38 Debug] IdentityServer4.Endpoints.UserInfoEndpoint

    End userinfo request

    测试角色授权

    counter页面增加认证要求,只要登录用户都能访问

    @attribute [Authorize]

    FetchData页面增加Admin角色授权要求,只有Admin角色可以访问

    @attribute [Authorize(Roles ="Admin")]

    alice登录,两个页面都可以访问,用bob登录,访问counter页面没问题,访问FetchData页面提示没有权限

    这样就可以用于真实的产品项目中去了。

    DEMO代码地址:https://gitee.com/woodsun/blzid4

  • 相关阅读:
    低水平博士是提升科研生涯的毒药
    MATLAB——axis
    MATLAB——PLOT绘图
    漫谈正态分布的生成
    一位创业者创业失败后,成功的做了一个创业孵化器!
    iOS 使用Method Swizzling隐藏Status Bar
    翻转View
    【iOS】Plist-XML-JSON数据解析
    Android流式布局实现
    S3C2416裸机开发系列十六_sd卡驱动实现
  • 原文地址:https://www.cnblogs.com/sunnytrudeau/p/15240461.html
Copyright © 2011-2022 走看看