• (十)React Ant Design Pro + .Net5 WebApi:后端环境搭建IdentityServer4(二)授权模式


    一、前言

    先交代一下整个Demo项目结构:

    • 一个认证服务(端口5000)IdentityServer4.Authentication
    • 五个授权模式(两个控制台程序,三个MVC项目端口5001)文件夹GrantClient
    • 两个资源服务(WebApi:UserApiResource端口8000,ProductApiResource端口9000)文件夹ApiResource

    二、准备认证服务 + 资源服务

    1、认证服务

    (1)新建一个MVC项目,安装 IdentityServer4 ,注册五种授权模式客户端,代码如下

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
    
            services.AddIdentityServer()
            .AddDeveloperSigningCredential()                            //临时证书
            .AddInMemoryClients(InMemoryConfig.GetClients())            //客户端模式,InMemory内存数据
            .AddInMemoryApiScopes(InMemoryConfig.GetApiScopes())        //作用域
            .AddInMemoryApiResources(InMemoryConfig.GetApiResources())  //资源
            .AddTestUsers(InMemoryConfig.GetTestUser())                 //用户
            .AddInMemoryIdentityResources(InMemoryConfig.IdentityResources);
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
    
            app.UseIdentityServer(); //使用IdentityServer4
    
            app.UseStaticFiles();
    
            app.UseRouting();
    
            app.UseAuthorization();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    
    public class InMemoryConfig
    {
        public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            //new IdentityResources.Email(),
            //new IdentityResources.Address(),
            //new IdentityResources.Phone()
        };
        /// <summary>
        /// ApiResource 资源列表
        /// </summary>
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new[]
            {
                new ApiResource("UserApiResource", "获取用户信息API")
                {
                    Scopes={ "UserScope" }
                },
                new ApiResource("ProductApiResource", "获取商品信息API")
                {
                    Scopes={ "ProductScope" }
                }
            };
        }
        /// <summary>
        /// ApiScopes 作用域
        /// </summary>
        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new ApiScope[]
            {
                new ApiScope("UserScope"),
                new ApiScope("ProductScope")
            };
        }
        /// <summary>
        /// Client 客户端
        /// </summary>
        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                //客户端模式
                new Client
                {
                    ClientId = "ClientCredentials",
                    ClientName = "ClientCredentials",
                    ClientSecrets = new [] { new Secret("ClientCredentials".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    AllowedScopes = new [] { "UserScope" }
                },
                //密码模式
                new Client
                {
                    ClientId = "ResourceOwnerPasswordCredentials",
                    ClientName = "ResourceOwnerPasswordCredentials",
                    ClientSecrets = new [] { new Secret("ResourceOwnerPasswordCredentials".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    AllowedScopes = new []
                    {
                        "ProductScope",
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                    }
                },
                //简化模式
                new Client
                {
                    ClientId = "Implicit",
                    ClientName = "Implicit",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    RedirectUris = { "https://localhost:5001/signin-oidc" },
                    PostLogoutRedirectUris = { "https://localhost:5001/signout-callback-oidc" },
                    RequireConsent = true,
                    AllowedScopes = new []{
                        "UserScope",
                        "ProductScope",
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                    }
                },
                //授权码模式
                new Client
                {
                    ClientId = "AuthorizationCode",
                    ClientName = "AuthorizationCode",
                    ClientSecrets = new [] { new Secret("AuthorizationCode".Sha256()) },
                    AllowedGrantTypes = GrantTypes.Code,
                    RedirectUris = { "https://localhost:5001/signin-oidc" },
                    PostLogoutRedirectUris = { "https://localhost:5001/signout-callback-oidc" },
                    RequireConsent = true,
                    AllowedScopes = new []{
                        "UserScope",
                        "ProductScope",
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                    }
                },
                //混合模式
                new Client
                {
                    ClientId = "Hybrid",
                    ClientName = "Hybrid",
                    ClientSecrets = new [] { new Secret("Hybrid".Sha256()) },
                    AllowedGrantTypes = GrantTypes.Hybrid,
                    RedirectUris = { "https://localhost:5001/signin-oidc" },
                    PostLogoutRedirectUris = { "https://localhost:5001/signout-callback-oidc" },
                    RequireConsent = true,
                    RequirePkce = false,
                    AllowedScopes = new []{
                        "UserScope",
                        "ProductScope",
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        //IdentityServerConstants.StandardScopes.Email,
                        //IdentityServerConstants.StandardScopes.Address,
                        //IdentityServerConstants.StandardScopes.Phone
                    }
                },
            };
        }
        public static List<TestUser> GetTestUser()
        {
            return new List<TestUser>(){
                new TestUser
                {
                    SubjectId = "1",
                    Username = "WinterSir",
                    Password = "WinterSir",
                    Claims =
                    {
                         new Claim(JwtClaimTypes.Name,"WinterSir"),
                         new Claim(JwtClaimTypes.GivenName,"WinterSir"),
                         new Claim(JwtClaimTypes.FamilyName,"WinterSir-FamilyName"),
                         new Claim(JwtClaimTypes.Email,"641187567@qq.com"),
                         new Claim(JwtClaimTypes.EmailVerified,"true", ClaimValueTypes.Boolean),
                         new Claim(JwtClaimTypes.WebSite,"http://WinterSir.com"),
                         new Claim(JwtClaimTypes.Address,@" [ 'street_address': 'Chang Ping', 'locality': 'BeiJing' ,'postal_code’: 102206,'country': 'China'}",
                         IdentityServerConstants.ClaimValueTypes.Json)
                    }
                }
            };
        }
    }
    

    (2)cmddotnet new is4ui安装Quickstart UI模板,删除原来 Controllers 中 HomeController 防止冲突,设置5000端口启动

    2、资源服务

    新建两个WebApi项目,安装IdentityServer4.AccessTokenValidation,分别修改Startup、Controller,设置8000、9000端口启动

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
    
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ProductApiResource", Version = "v1" });
            });
    
            //集成端口为5000的认证服务
            services.AddAuthentication("Bearer")
              .AddIdentityServerAuthentication(options =>
              {
                  options.Authority = "https://localhost:5000";
                  options.ApiName = "ProductApiResource";
              });
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ProductApiResource v1"));
            }
    
            app.UseRouting();
    
            app.UseAuthentication();//鉴权
    
            app.UseAuthorization();//授权
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
    

    三、授权模式

    1、客户端授权模式

    客户端模式(Client Credentials)指客户端以自己的名义,而不是以用户的名义,向"认证服务"进行认证。如果是提前约束好的客户端,直接给你颁发令牌 token

    安装IdentityModel

    class Program
    {
        /// <summary>
        /// 客户端模式(Client Credentials)
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.WriteLine("***************** 客户端模式(Client Credentials)*****************");
            var client = new HttpClient();
            var disco = client.GetDiscoveryDocumentAsync("https://localhost:5000/").Result;
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return;
            }
            var tokenResponse = client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = "ClientCredentials",
                ClientSecret = "ClientCredentials",
                Scope = "UserScope"
            }).Result;
    
            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return;
            }
    
            Console.WriteLine("\nToken: " + tokenResponse.AccessToken);
    
            var apiClient = new HttpClient();
            apiClient.SetBearerToken(tokenResponse.AccessToken);
            var response = apiClient.GetAsync("https://localhost:8000/User/Get").Result;
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
            }
            else
            {
                var content = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine("\n结果: " + content);
            }
    
            Console.ReadLine();
        }
    }
    

    2、密码模式

    密码模式(Resource Owner Password Credentials)中客户端使用用户提供的用户名和密码,向"认证服务"进行认证,有较高风险,通常只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。相较于客户端多了一个用户角色。

    安装IdentityModel

    static void Main(string[] args)
    {
        Console.WriteLine("***************** 密码模式(Resource Owner Password credentials)***************** ");
        var client = new HttpClient();
        var disco = client.GetDiscoveryDocumentAsync("https://localhost:5000/").Result;
        if (disco.IsError)
        {
            Console.WriteLine(disco.Error);
            return;
        }
        var tokenResponse = client.RequestPasswordTokenAsync(new PasswordTokenRequest()
        {
            Address = disco.TokenEndpoint,
            ClientId = "ResourceOwnerPasswordCredentials",
            ClientSecret = "ResourceOwnerPasswordCredentials",
            UserName = "WinterSir",
            Password = "WinterSir",
            Scope = "ProductScope",
        }).Result;
    
        if (tokenResponse.IsError)
        {
            Console.WriteLine(tokenResponse.Error);
            return;
        }
    
        Console.WriteLine("\nToken: " + tokenResponse.AccessToken);
    
        var apiClient = new HttpClient();
        apiClient.SetBearerToken(tokenResponse.AccessToken);
        var response = apiClient.GetAsync("https://localhost:9000/Product/Get").Result;
        if (!response.IsSuccessStatusCode)
        {
            Console.WriteLine(response.StatusCode);
        }
        else
        {
            var content = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine("\n结果: " + content);
        }
    
        Console.ReadLine();
    }
    

    3、简化模式

    简化模式(Implicit)比授权码模式少了code环节,所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证,该模式是很不安全的,且不支持refresh token,适用于 Web 安全要求不高的场景,设置较短时效的 token。

    (1)安装IdentityServer4.AccessTokenValidation、Microsoft.AspNetCore.Authentication.OpenIdConnect,修改Startup

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //关闭Jwt映射
            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
            //注册授权
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = "https://localhost:5000";       //认证服务
                options.RequireHttpsMetadata = true;                //必须使用Https,否则用户无法登录
                options.ClientId = "Implicit";
                options.ClientSecret = "Implicit";
                options.SaveTokens = true; //表示Token要存储
            });
    
            services.AddControllersWithViews().AddRazorRuntimeCompilation();
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
    
            app.UseRouting();
    
            app.UseAuthentication();
    
            app.UseAuthorization();
    
            app.UseHttpsRedirection();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    

    (2)_Layout.cshtml添加 注销按钮

    <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
        <ul class="navbar-nav flex-grow-1">
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
            </li>
        </ul>
        <a class="nav-link text-dark float-right" asp-area="" asp-controller="Home" asp-action="Logout">Logout</a>
    </div>
    

    (3)HomeController添加对应功能,需要认证的方法加上特性[Authorize]

    [Authorize]
    public IActionResult Privacy()
    {
        return View();
    }
    
    //注销
    public IActionResult Logout()
    {
        return SignOut("Cookies", "oidc");
    }
    

    (4)修改Privacy.cshtml

    @{
        ViewData["Title"] = "Privacy Policy";
    }
    <h1>@ViewData["Title"]</h1>
    
    @using Microsoft.AspNetCore.Authentication
    
    <h2>Claims</h2>
    
    <dl>
        @foreach (var claim in User.Claims)
        {
            <dt>@claim.Type</dt>
            <dd>@claim.Value</dd>
        }
    </dl>
    
    <h2>Properties</h2>
    
    <dl>
        @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
        {
            <dt>@prop.Key</dt>
            <dd>@prop.Value</dd>
        }
    </dl>
    

    (5)效果图

    4、授权码模式

    授权码模式(Authorization Code)不同于简化模式直接返回token,而是先返回一个授权码,再用授权码去请求token,然后携带访问Api资源。授权码模式是功能最完整、流程最严密的授权模式。

    (1)安装IdentityServer4.AccessTokenValidation、Microsoft.AspNetCore.Authentication.OpenIdConnect,修改Startup

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //关闭Jwt映射
            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
            //注册授权
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = "https://localhost:5000";       //认证服务
                options.RequireHttpsMetadata = true;                //必须使用Https,否则用户无法登录
                options.ClientId = "AuthorizationCode";
                options.ClientSecret = "AuthorizationCode";
                options.ResponseType = "code";
                options.Scope.Clear();
                options.Scope.Add("UserScope");
                options.Scope.Add("ProductScope");
                options.Scope.Add(OidcConstants.StandardScopes.OpenId);
                options.Scope.Add(OidcConstants.StandardScopes.Profile);
                //options.Scope.Add(OidcConstants.StandardScopes.Email);
                //options.Scope.Add(OidcConstants.StandardScopes.Phone);
                //options.Scope.Add(OidcConstants.StandardScopes.Address);
                options.SaveTokens = true; //表示Token要存储
            });
    
            services.AddControllersWithViews().AddRazorRuntimeCompilation();
            services.AddControllers().AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
            });
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
    
            app.UseRouting();
    
            app.UseAuthentication();
    
            app.UseAuthorization();
    
            app.UseHttpsRedirection();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    

    (2)_Layout.cshtml添加 获取用户按钮、注销按钮

    <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
        <ul class="navbar-nav flex-grow-1">
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="User">UserApi</a>
            </li>
        </ul>
        <a class="nav-link text-dark float-right" asp-area="" asp-controller="Home" asp-action="Logout">Logout</a>
    </div>
    

    (3)HomeController添加对应功能,需要认证的方法加上特性[Authorize]

    [Authorize]
    public IActionResult Privacy()
    {
        return View();
    }
    
    [Authorize]
    public async Task<IActionResult> User()
    {
        var client = new HttpClient();
        var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
        if (string.IsNullOrEmpty(accessToken))
        {
            return Json(new { msg = "accesstoken 获取失败" });
        }
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        var httpResponse = await client.GetAsync("https://localhost:8000/User/Get");
        var result = await httpResponse.Content.ReadAsStringAsync();
        if (!httpResponse.IsSuccessStatusCode)
        {
            ViewBag.Result = new { msg = "请求 User/Get 失败", error = result };
        }
        ViewBag.Result = new { msg = "成功", data = result };
        return View();
    }
    
    //注销
    public IActionResult Logout()
    {
        return SignOut("Cookies", "oidc");
    }
    

    (4)修改Privacy.cshtml

    @{
        ViewData["Title"] = "Privacy Policy";
    }
    <h1>@ViewData["Title"]</h1>
    
    @using Microsoft.AspNetCore.Authentication
    
    <h2>Claims</h2>
    
    <dl>
        @foreach (var claim in User.Claims)
        {
            <dt>@claim.Type</dt>
            <dd>@claim.Value</dd>
        }
    </dl>
    
    <h2>Properties</h2>
    
    <dl>
        @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
        {
            <dt>@prop.Key</dt>
            <dd>@prop.Value</dd>
        }
    </dl>
    

    (5)效果图

    5、混合模式

    混合模式(Hybrid Flow)

    它为我们提供了两全其美的优势,身份令牌通过浏览器传输,因此客户端可以在进行任何更多工作之前对其进行验证。如果验证成功,客户端会通过令牌服务的以获取访问令牌

    (1)安装IdentityServer4.AccessTokenValidation、Microsoft.AspNetCore.Authentication.OpenIdConnect,修改Startup

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //关闭Jwt映射
            JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
            //注册授权
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = "https://localhost:5000";       //认证服务
                options.RequireHttpsMetadata = true;                //必须使用Https,否则用户无法登录
                options.ClientId = "Hybrid";
                options.ClientSecret = "Hybrid";
                options.ResponseType = "code id_token";
                options.Scope.Clear();
                options.Scope.Add("UserScope");
                options.Scope.Add("ProductScope");
                options.Scope.Add(OidcConstants.StandardScopes.OpenId);
                options.Scope.Add(OidcConstants.StandardScopes.Profile);
                //options.Scope.Add(OidcConstants.StandardScopes.Email);
                //options.Scope.Add(OidcConstants.StandardScopes.Phone);
                //options.Scope.Add(OidcConstants.StandardScopes.Address);
                //options.Scope.Add(OidcConstants.StandardScopes.0fflineAccess);//获取到刷新Token
                options.SaveTokens = true; //表示Token要存储
            });
    
            services.AddControllersWithViews().AddRazorRuntimeCompilation();
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
    
            app.UseRouting();
    
            app.UseAuthentication();
    
            app.UseAuthorization();
    
            app.UseHttpsRedirection();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
    

    (2)_Layout.cshtml添加 获取产品、注销按钮

    <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
        <ul class="navbar-nav flex-grow-1">
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Product">ProductApi</a>
            </li>
        </ul>
        <a class="nav-link text-dark float-right" asp-area="" asp-controller="Home" asp-action="Logout">Logout</a>
    </div>
    

    (3)HomeController添加对应功能,需要认证的方法加上特性[Authorize]

    [Authorize]
    public IActionResult Privacy()
    {
        return View();
    }
    [Authorize]
    public async Task<IActionResult> Product()
    {
        var client = new HttpClient();
        var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
        if (string.IsNullOrEmpty(accessToken))
        {
            return Json(new { msg = "accesstoken 获取失败" });
        }
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        var httpResponse = await client.GetAsync("https://localhost:9000/Product/Get");
        var result = await httpResponse.Content.ReadAsStringAsync();
        if (!httpResponse.IsSuccessStatusCode)
        {
            ViewBag.Result = new { msg = "请求 User/Get 失败", error = result };
        }
        ViewBag.Result = new { msg = "成功", data = result };
        return View();
    }
    
    //注销
    public IActionResult Logout()
    {
        return SignOut("Cookies", "oidc");
    }
    

    (4)修改Privacy.cshtml

    @{
        ViewData["Title"] = "Privacy Policy";
    }
    <h1>@ViewData["Title"]</h1>
    
    @using Microsoft.AspNetCore.Authentication
    
    <h2>Claims</h2>
    
    <dl>
        @foreach (var claim in User.Claims)
        {
            <dt>@claim.Type</dt>
            <dd>@claim.Value</dd>
        }
    </dl>
    
    <h2>Properties</h2>
    
    <dl>
        @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
        {
            <dt>@prop.Key</dt>
            <dd>@prop.Value</dd>
        }
    </dl>
    

    (5)效果图

    四、问题踩坑

    1、Https

    Demo全部用的Https,Mvc客户端配置RequireHttpsMetadata = true如果使用http遇到认证服无法务登录问题,可参考以下地址
    https://www.cnblogs.com/i3yuan/p/14033016.html#autoid-20-0-0

    2、ResponseType

    授权码模式、混合模式需要修改客户端配置ResponseType,ResponseType = "code" 、 ResponseType = "code id_token"

    3、RequirePkce

    混合模式需要修改对应服务端注册客户端时配置RequirePkce = false,这样不需要客户端提供code challeng

    4、其他Error

    出现错误大概率是客户端、服务端配置项问题,仔细对比一下就OK了

    五、前人栽树,后人乘凉

    https://www.cnblogs.com/i3yuan/category/1777690.html

    六、代码已上传

    https://github.com/WinterSir/IdentityServer4.GrantTypesDemo

  • 相关阅读:
    冒泡排序
    Window中常见的dos命令
    spring boot 重定向
    阿里云轻量级服务器使用
    网络知识
    spring boot security 登出
    深入理解java虚拟机
    jsp内置对象与servlet的关系
    求一个有向无换图中,最长简单路径。动态规划问题15-1
    一些动态规划问题的java实现
  • 原文地址:https://www.cnblogs.com/WinterSir/p/16044639.html
Copyright © 2020-2023  润新知