• 欢迎光临~

IdentityServer4+.net core+Docker认证授权和单点登录

开发技术 开发技术 2022-05-30 次浏览

操作环境:Centos7.8+.net Core3.1+Docker

由于IdentityServer4的认证授权功能太过强大和复杂,实现了OAuth2.0的四种授权模式——隐式模式(implicit)、授权码模式(Authorization Code)、密码凭证模式(Resource Owner Password Credentials)、客户端凭证模式(Client Credentials),本文仅以其中的客户端凭证模式和隐式模式,实现两种不同场景作为示例,供大家参考学习。

一、客户端凭证模式——实现接口资源认证授权

示例场景:Web应用(MVC)请求微服务接口资源(WebAPI)需要通过idetityserver认证中心授权(采用客户端凭证模式实现)

项目整体结构:

IdentityServer4+.net core+Docker认证授权和单点登录

1、认证中心IdentityServer项目

使用Nuget引入IdentityServer4组件

IdentityServer4+.net core+Docker认证授权和单点登录

创建Config配置类

using IdentityServer4;
using IdentityServer4.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HPM.IdentityServer4
{
    public class Config
    {
        /// <summary>
        /// 获取自定义API资源列表
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApiResources()
        {
            List<ApiResource> apiResources = new List<ApiResource>();
            apiResources.Add(new ApiResource() { Name = "ProductMicroService", DisplayName = "产品微服务",Scopes = { "ProductMicroService" } });//定义资源名称
            apiResources.Add(new ApiResource() { Name = "UserMicroService", DisplayName = "用户微服务", Scopes = { "UserMicroService" } });//定义资源名称

            return apiResources;
        }

        /// <summary>
        /// 获取自定义客户端配置列表
        /// 支持 授权码,隐藏式,密码式,凭证式、混合式
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            List<Client> clients = new List<Client>();

            //客户端凭证授权模式(用于客户端访问微服务API资源授权认证)
            clients.Add(new Client()
            {
                ClientId="client",//定义客户端获取Token时指定的client_id值
                AllowedGrantTypes=GrantTypes.ClientCredentials,//指定grant_type为client_credentials客户端授权模式
                ClientSecrets = {new Secret("Secret".Sha256())},//定义客户端获取token时指定的client_secret值
                AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true
                AccessTokenLifetime = 3600,//token有效期,默认3600秒
                AllowedScopes = { "ProductMicroService", "UserMicroService" }//设置可访问范围的资源名称
            });return clients;
        }

        /// <summary>
        /// 获取IdentityServer4自身API资源列表
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() };
        }

        public static IEnumerable<ApiScope> GetApiScopes()
        {
            return new List<ApiScope> { new ApiScope("ProductMicroService"), new ApiScope("UserMicroService") };
        }

    }
}

修改StartUp类

在ConfigureServices方法中添加IdentityServer的依赖注入

services.AddIdentityServer().AddDeveloperSigningCredential()
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiScopes(Config.GetApiScopes())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients());

在Configure方法中启用IdentityServer中间件及认证授权

// 配置管道中间件
        // 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();
            }

            //暂时禁用Https
            //app.UseHttpsRedirection();

            app.UseStaticFiles();

            app.UseRouting();

            //身份认证中间件(身份验证必须在授权的前面)
            app.UseAuthentication();

            //授权中间件
            app.UseAuthorization();

            //这个必须在UseRouting和UseEndpoints中间。如果IdentityServer服务端和API端要写在一起,
            //那么这个必须在UseAuthorization和UseAuthentication的上面。
            //通过访问https://localhost:端口/.well-known/openid-configuration默认配置地址可以查看IdentityServer提供的endpoint
            app.UseIdentityServer();//使用IdentityServer中间件

            app.UseEndpoints(endpoints =>
            {
                //endpoints.MapControllers();
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

2、微服务ProductMicroService接口资源项目(其他微服务同样调整)

修改StartUp类

在ConfigureServices方法中添加IdentityServer的认证授权依赖注入

//指定使用IdentityServer作为API资源的授权模式
            services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options => {
                options.Authority = "https://www.aaa.vip:5050";//设置IdentityServer授权端口地址
                options.ApiName = "ProductMicroService";//设置要开放访问的资源名称
                options.RequireHttpsMetadata = false;
            });

在Configure方法中启用认证授权中间件

            //身份验证中间件 (身份验证必须在授权的前面)
            app.UseAuthentication();

            //授权验证中间件
            app.UseAuthorization();   

然后在需要通过授权验证才能访问的API加上[Authorize]特性保护起来

IdentityServer4+.net core+Docker认证授权和单点登录

3、Web应用客户端项目

Web客户端应用想要请求到微服务中带有[Authorize]特性标识的API资源,则必须在请求的同时,提供IdentityServer认证中心颁发的令牌,因此最好封装一个获取Token令牌的方法,如下

public async Task<string> GetAccessToken(string client_id,string client_secret,string scope)
        {
            //获取Redis中缓存的Token
            string accessToken = _redisService.GetTokenValue(scope);
            //string accessToken = _redisService.GetValue(scope + "_access_token");
            if (string.IsNullOrEmpty(accessToken))
            {
                //重新从Identityserver中获取token
                var tokenClient = new HttpClient();
                var tokenResponse = await tokenClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
                {
                    Address = "https://www.aaa.vip:5100" + "/connect/token",

                    ClientId = client_id,
                    ClientSecret = client_secret,
                    Scope = scope
                });

                accessToken = tokenResponse.AccessToken;
                //设置redis中的token字符串600秒过期
                _redisService.SetTokenValue(scope, accessToken);
                //_redisService.SetValue(scope + "_access_token", accessToken, 600);
            }
            

            ////获取Refresh_Token
            //tokenResponse = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest
            //{
            //    Address = identityServerUrl + "connect/token",

            //    ClientId = client_id,
            //    ClientSecret = client_secret,
            //    Scope = scope
            //});

            return accessToken;
        }

其中client_id、client_secret、scope参数分别是IdentityServer项目中配置类Config中定义的Client客户端信息,讲这些客户端ID和密钥信息保持一致传入即可获取AccessToken授权令牌。

然后在请求微服务接口资源时,将令牌带入请求头部即可,如下

IdentityServer4+.net core+Docker认证授权和单点登录

 

 

二、隐式模式——实现单点登录

首先要想使用IdentityServer实现单点登录,必须要满足一个条件,那就是应用站点和认证站点必须是HTTPS,重要的事情说三遍,必须是HTTPS!必须是HTTPS!必须是HTTPS!否则登录认证回调环节会有问题,过不去。

至于怎么搭建.net core的HTTPS站点,我在之后其他的文章中会讲解,并不复杂,主要是要花钱,没有氪金心理准备的就别玩IdentityServer的单点登录了。

示例场景:多个Web应用(mvc)通过请求IdentityServer认证中心实现单点登录(本文暂时仅以单个客户端应用进行演示)

项目整体结构:

IdentityServer4+.net core+Docker认证授权和单点登录

1、认证中心IdentityServer项目

使用Nuget引入IdentityServer4组件

IdentityServer4+.net core+Docker认证授权和单点登录

创建Config配置类

using IdentityServer4;
using IdentityServer4.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HPM.IdentityServer4
{
    public class Config
    {

        /// <summary>
        /// 获取自定义客户端配置列表
        /// 支持 授权码,隐藏式,密码式,凭证式、混合式
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            List<Client> clients = new List<Client>();

            //隐藏式授权模式(用于多客户端单点登录)
            clients.Add(new Client()
            {
                ClientId = "hpm_mvc_imp",
                ClientName = "hpm",
                AllowedGrantTypes = GrantTypes.Implicit,
                //设置是否显示授权提示界面
                //RequireConsent=true,

                //指定允许令牌或授权码返回的地址(URL)-登录成功后重定向地址
                //RedirectUris = { "http://www.b.net:5001/signin-oidc", "http://www.a.cn:5002/signin-oidc" },
                RedirectUris = { "https://www.aaa.vip:5050/signin-oidc" },
                //指定允许注销后返回的地址(URL),这里写两个客户端-注销成功后的重定向地址
                //PostLogoutRedirectUris = { "http://www.b.net:5001/signout-callback-oidc", "http://www.a.cn:5002/signout-callback-oidc" },
                PostLogoutRedirectUris = { "https://www.aaa.vip:5050/signout-callback-oidc" },
                ClientSecrets = { new Secret("SSOSecret".Sha256()) },
                AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                    }
            });

            return clients;
        }

        /// <summary>
        /// 获取IdentityServer4自身API资源列表
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() };
        }


    }
}

修改StartUp类

在ConfigureServices方法中添加IdentityServer的依赖注入

#region 添加IdentityServer单点登录依赖注入,要使用IdentityServer的单点登录,则必须启用https
            //我们使用的IdentityModel这个组件,它默认限制了当 Ids4 非localhost地址时,必须启用HTTPS。详情见https://ask.csdn.net/questions/753220
            //同时谷歌浏览器,对于cookies的写入,也必须要求https
            services.AddIdentityServer(options =>
            {
                //可以通过此设置来指定登录路径,默认的登陆路径是/account/login
                options.UserInteraction.LoginUrl = "/Account/Login";//【必备】登录地址  
                options.UserInteraction.LogoutUrl = "/Account/Logout";//【必备】退出地址 
                //options.UserInteraction.ConsentUrl = "/Account/Consent";//【必备】允许授权同意页面地址
                //options.UserInteraction.ErrorUrl = "/Account/Error";//【必备】错误页面地址
                options.UserInteraction.LoginReturnUrlParameter = "ReturnUrl";//【必备】设置传递给登录页面的返回URL参数的名称。默认为returnUrl 
                options.UserInteraction.LogoutIdParameter = "logoutId"; //【必备】设置传递给注销页面的注销消息ID参数的名称。缺省为logoutId 
                //options.UserInteraction.ConsentReturnUrlParameter = "ReturnUrl"; //【必备】设置传递给同意页面的返回URL参数的名称。默认为returnUrl
                //options.UserInteraction.ErrorIdParameter = "errorId"; //【必备】设置传递给错误页面的错误消息ID参数的名称。缺省为errorId
                //options.UserInteraction.CustomRedirectReturnUrlParameter = "ReturnUrl"; //【必备】设置从授权端点传递给自定义重定向的返回URL参数的名称。默认为returnUrl
                //options.UserInteraction.CookieMessageThreshold = 5; //【必备】由于浏览器对Cookie的大小有限制,设置Cookies数量的限制,有效的保证了浏览器打开多个选项卡,一旦超出了Cookies限制就会清除以前的Cookies值

            }).AddDeveloperSigningCredential()
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryClients(Config.GetClients());

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
  .AddCookie(options =>
  { // all your options
      options.Cookie.HttpOnly = false;
      // in dev
      options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
      //options.Cookie.SecurePolicy = CookieSecurePolicy.None;
      //谷歌浏览器对SameSite有安全要求,否则无法写入Cookie,详情见https://stackoverflow.com/questions/51912757/identity-server-is-keep-showing-showing-login-user-is-not-authenticated-in-c
      //options.Cookie.SameSite = SameSiteMode.None;
      options.Cookie.SameSite = SameSiteMode.Lax;
  });

            #endregion

在Configure方法中启用IdentityServer中间件及认证授权

// 配置管道中间件
        // 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();
            }

            //暂时禁用Https
            //app.UseHttpsRedirection();

            app.UseStaticFiles();

            app.UseRouting();

            //身份认证中间件(身份验证必须在授权的前面)
            app.UseAuthentication();

            //授权中间件
            app.UseAuthorization();

            //这个必须在UseRouting和UseEndpoints中间。如果IdentityServer服务端和API端要写在一起,
            //那么这个必须在UseAuthorization和UseAuthentication的上面。
            //通过访问https://localhost:端口/.well-known/openid-configuration默认配置地址可以查看IdentityServer提供的endpoint
            app.UseIdentityServer();//使用IdentityServer中间件

            app.UseEndpoints(endpoints =>
            {
                //endpoints.MapControllers();
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

创建登录及注销控制器,其中/Account/Login和/Account/Logout分别是StartUp的ConfigureServices中给IdentityServer设定好的登入和登出时自动回调的地址(即MVC里的Action)。这2个地址(Action)非常重要。除此之外,一般还会有一个处理登录信息提交的Action,也很重要。由于太过重要,我就直接将整段代码贴出来

public class AccountController : Controller
    {
        private readonly IIdentityServerInteractionService _interaction;
        private readonly IUserServiceClient _userServiceClient;//自定义用户信息服务

        public AccountController(IIdentityServerInteractionService interaction, IUserServiceClient userServiceClient)
        {
            this._interaction = interaction;
            this._userServiceClient = userServiceClient;
        }

        /// <summary>
        /// 登录页加载
        /// 登录时IdentityServer自动跳转到此登录页
        /// </summary>
        /// <param name="ReturnUrl"></param>
        /// <returns></returns>
        public IActionResult Login(string ReturnUrl)
        {
            if (HttpContext.User.Identity.IsAuthenticated)
            {
                return Redirect(ReturnUrl);
            }
            LoginInputViewModel vm = new LoginInputViewModel()
            {
                ReturnUrl = ReturnUrl
            };
            return View(vm);
        }

        /// <summary>
        /// 提交登录信息操作
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> Login(LoginInputViewModel model)
        {
            //当登录提交给后台的model为null,则返回错误信息给前台
            if (model == null)
            {
                ViewData["TipMessage"] = "登录失败,数据为空!";
                return View(model);
            }
            //这里同理,当信息不完整的时候,返回错误信息给前台
            if (string.IsNullOrEmpty(model.Username) || string.IsNullOrEmpty(model.Password))
            {
                //这里只是简单处理了
                ViewData["TipMessage"] = "登录失败,用户名和密码不能为空!";
                return View(model);
            }

            //自定义的方法获取数据库信息验证用户名和密码
            BaseResultModel<UserInfoForLoginDto> loginResult = await _userServiceClient.GetLoginUserInfo(model.Username, model.Password);
            if (loginResult.success)
            {
                //登录用户信息
                UserInfoForLoginDto loginUserInfo = loginResult.data;

                //配置Cookie
                AuthenticationProperties properties = new AuthenticationProperties()
                {
                    IsPersistent = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30))
                    //ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) //记住登录
                };

                //使用IdentityServerUser来进行SignInAsync
                // issue authentication cookie with subject ID and username
                var isuser = new IdentityServerUser(loginUserInfo.Id.ToString())
                {
                    DisplayName = loginUserInfo.UserName
                };
                await HttpContext.SignInAsync(isuser, properties);

                //使用IIdentityServerInteractionService的IsValidReturnUrl来验证ReturnUrl是否有问题
                if (_interaction.IsValidReturnUrl(model.ReturnUrl))
                {
                    return Redirect(model.ReturnUrl);
                }
                else
                {
                    ViewData["TipMessage"] = "跳转地址有误:" + model.ReturnUrl;
                    return View(model);
                }
            }
            else
            {
                //return Content("<script >alert('"+ loginResult.message + "');</script>", "text/html");
                ViewData["TipMessage"] = loginResult.message;
                return View(model);
            }

            return View();
        }

        /// <summary>
        /// 登录注销
        /// 注销登录时IdentityServer会自动回调此路径
        /// </summary>
        /// <param name="logoutId"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> Logout(string logoutId)
        {
            //var logoutId = await _interaction.CreateLogoutContextAsync();
            Console.WriteLine("准备注销登录,logoutId为:" + logoutId);
            var logout= await _interaction.GetLogoutContextAsync(logoutId);
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            if (logout.PostLogoutRedirectUri != null)
            {
                Console.WriteLine("注销登录,跳转PostLogoutRedirectUri地址:" + logout.PostLogoutRedirectUri);
                return Redirect(logout.PostLogoutRedirectUri);
            }
            var refererUrl = Request.Headers["Referer"].ToString();
            return Redirect(refererUrl);
        }

    }

这其中登录Login的ReturnUrl参数,以及注销Logout的logoutId参数,可以不用管,因为它们是IdentityServer回调地址,IdentityServer会自动带上这2个参数跳转到这2个地址。

至于是从何处触发的IdentityServer回调跳转?当然是在客户端Web应用的控制器中触发,下面会慢慢讲到。

为了便于大家理解,这里我将登录页、登录页的视图模型类ViewModel也一并贴出来

    /// <summary>
    /// 登录页的视图模型
    /// </summary>
    public class LoginInputViewModel
    {
        [Required]
        public string Username { get; set; }
        [Required]
        public string Password { get; set; }
        public bool RememberLogin { get; set; }
        public string ReturnUrl { get; set; }
    }
            <div class="login_con">
                <h1>登录/LOGIN</h1>
                @using (Html.BeginForm("Login", "Account"))
                {
                    <div class="text_box">
                        <div>
                            <input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
                            <div><i><img src="/login/images/icon01.png"></i><input id="name" type="text" name="Username" placeholder="请输入注册邮箱"></div>
                            <div><i><img src="/login/images/icon02.png"></i><input id="pwd" type="password" name="Password" placeholder="请输入密码"></div>
                            <button type="submit">登 录</button>
                        </div>
                    </div>
                }

            </div>

 

2、Web应用客户端项目

修改StartUp类

在ConfigureServices方法中添加IdentityServer的依赖注入

//注入IdentityServer单点登录授权认证
            //DefaultChallengeScheme的名字要和下面AddOpenIdConnect方法第一个参数名字保持一致
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;//默认的身份验证方案名
                options.DefaultChallengeScheme = "oidc";//用来跳转到ids登录页的身份验证方案名
                //注意这俩配置与下面注册的身份验证方案的名字对应
            })
           .AddCookie(options =>
           {
               options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
               options.Cookie.Name = CookieAuthenticationDefaults.AuthenticationScheme;//注册asp.net core 默认的基于cookie的身份验证方案
           })
           .AddOpenIdConnect("oidc", options =>//注册ids为我们提供的oidc身份验证方案
           {
               options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.Authority = "https://www.aaa.vip:5100";
               options.RequireHttpsMetadata = false;

               //指定允许服务端返回的地址,默认是new PathString("/signin-oidc")
               //如果这里地址进行了自定义,那么服务端也要进行修改
               //options.CallbackPath = new PathString("/signin-oidc");

               //指定用户注销后,服务端可以调用客户端注销的地址,默认是new PathString("signout-callback-oidc")
               //options.SignedOutCallbackPath = new PathString("/signout-callback-oidc");

               options.ClientId = "hpm_mvc_imp";
               options.ClientSecret = "SSOSecret";
               //options.ResponseType = "code id_token";//授权模式
               options.SaveTokens = true;//是否将最后获取的idToken和accessToken存储到默认身份验证方案中

               //布尔值来设置处理程序是否应该转到用户信息端点检索。额外索赔或不在id_token创建一个身份收到令牌端点。默认为“false”
               //options.GetClaimsFromUserInfoEndpoint = true;

               //事件
               options.Events = new Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents()
               {
                   //远程故障
                   OnRemoteFailure = context =>
                   {
                       context.Response.Redirect("/SSO/Error");
                       context.HandleResponse();
                       return Task.FromResult(0);
                   },
                   //访问拒绝
                   OnAccessDenied = context =>
                   {
                       //重定向到指定页面
                       context.Response.Redirect("/SSO/Denied");
                       //停止此请求的所有处理并返回给客户端
                       context.HandleResponse();
                       return Task.FromResult(0);
                   },
               };
           });

在Configure方法中启用认证授权中间件及Cookie策略中间件

            //使用Cookie
            app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Lax });

            app.UseRouting();

            //身份认证中间件(身份验证必须在授权的前面)
            app.UseAuthentication();

            app.UseAuthorization();

接着,在任何需要登录验证的Action操作上,添加[Authorize]特性标识,则未登录状态下系统会触发认证,自动跳转到identityServer项目的登录页/Account/Login并带上相应ReturnUrl参数,如下

IdentityServer4+.net core+Docker认证授权和单点登录

如果不想这样被动的触发登录跳转,想要实现主动点击【登录】按钮跳转到登录页,可以创建一个用户控制器,将登录和注销功能都写在里面,其中登录Action带上[Authorize]特性标识,这样就能巧妙实现触发登录页跳转。

    public class UserController : Controller
    {/// <summary>
        /// 登录
        /// </summary>
        /// <param name="pageUrl"></param>
        /// <returns></returns>
        [Authorize]
        public IActionResult Login(string pageUrl=null)
        {
            //在认证中心登录页登录后,回到此处再跳转到其他页面
            if(pageUrl == null)
            {
                return RedirectToAction("Index", "Home");
            }
            else
            {
                return Redirect(pageUrl);
            }
        }

        /// <summary>
        /// 注销登录
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public IActionResult Logout()
        {
            //this.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            //this.HttpContext.SignOutAsync("oidc");
            //return RedirectToAction("Index", "Home");

            //这里会触发identityServer对/Account/Logout的回调,并带上logoutId参数
            return SignOut("Cookies", "oidc");
        }

    }

客户端主动触发登录注销操作的位置

                @if (string.IsNullOrEmpty(Model.UserName))
                {
                    <div id="loginBox">
                        <a style="cursor: pointer;" href="/User/Login">登录</a><span> | </span><a style="cursor: pointer;" href="login/register.html">注册</a>
                    </div>
                }
                else
                {
                    <span>Hi @Model.UserName 欢迎回来~<br /><a style="cursor: pointer;" href="/User/Logout">退出登录</a></span>
                }

当在IdentityServer认证中心的登录页登录成功后,会自动跳转回Web客户端项目,这时客户端需要获取登录后的用户信息,获取方式如下:

            if(HttpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.NameIdentifier)!=null)
            {
                //Type:http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier Value:1
                viewModel.UserId = Convert.ToInt32(HttpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.NameIdentifier).Value);//获取用户ID
                //Type:name Value:一人之下
                viewModel.UserName = HttpContext.User.Claims.SingleOrDefault(s => s.Type == "name").Value;//获取用户姓名
            }     

直接在客户端的控制器中获取即可,这里由于我在登录时设定的IdentityServerUser参数Type是这2个,所以就用这2个Type来获取对应的信息,实际开发过程中大家可以根据自己的需要去设置,不一定要按照我的Type来取。

 

至此,费了我九牛二虎之力,苦心钻研一周的IdentityServer单点登录算是讲完了,最后还是要提醒大家一句,玩IdentityServer单点登录的前提是,你的站点必须是HTTPS!!!

本人虽然在钻研过程中参考了不少资料,但此文基本也算是原创,麻烦大家转载时注明出处,祝各位工作顺利,少死脑细胞。

 

程序员灯塔
转载请注明原文链接:IdentityServer4+.net core+Docker认证授权和单点登录
喜欢 (0)