IdentityServer4 实现单点登录
服务端
public class Config { /// <summary> /// IDS资源 /// </summary> /// <returns></returns> public static IEnumerable<IdentityResource> GetIds() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; } public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("UserCenter", "UserCenter API") }; } public static IEnumerable<ApiScope> GetApiScopes() { return new List<ApiScope> { new ApiScope("UserCenter", "UserCenter API") }; } /// <summary> /// 可以使用ID4 Server 客户端资源 /// </summary> /// <returns></returns> public static IEnumerable<Client> GetClients() { List<Client> clients = new List<Client>() { ////授权码模式 //new Client //{ // ClientId = "client1", // ClientSecrets = { new Secret("123456".Sha256()) }, // //授权码模式 // AllowedGrantTypes = GrantTypes.Code, // //需要确认授权 // RequireConsent = true, // RequirePkce = true, // //允许token通过浏览器 // AllowAccessTokensViaBrowser=true, // // where to redirect to after login(登录) // RedirectUris = { "https://localhost:7072/signin-oidc" }, // // where to redirect to after logout(退出) // PostLogoutRedirectUris = { "https://localhost:7072/signout-callback-oidc" }, // //允许的范围 // AllowedScopes = new List<string> // { // IdentityServerConstants.StandardScopes.OpenId, // IdentityServerConstants.StandardScopes.Profile, // "UserCenter" // }, // AlwaysIncludeUserClaimsInIdToken=true //}, new Client { ClientId = "client2", ClientSecrets = { new Secret("123456".Sha256()) }, //混合与客户端模式 AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, //需要确认授权 RequireConsent = true, RequirePkce = false, //允许token通过浏览器 AllowAccessTokensViaBrowser=true, // where to redirect to after login(登录) RedirectUris = { "https://localhost:7072/signin-oidc" }, // where to redirect to after logout(退出) PostLogoutRedirectUris = { "https://localhost:7072/signout-callback-oidc" }, AccessTokenLifetime = 3600,//Token 过期时间 //AccessTokenType= AccessTokenType.Jwt, AlwaysIncludeUserClaimsInIdToken = true,//将用户所有的claims包含在IdToken内 //AbsoluteRefreshTokenLifetime = 2592000,//RefreshToken的最长生命周期,默认30天 //RefreshTokenExpiration = TokenExpiration.Sliding,//刷新令牌时,将刷新RefreshToken的生命周期。RefreshToken的总生命周期不会超过AbsoluteRefreshTokenLifetime。 //SlidingRefreshTokenLifetime = 3600,//以秒为单位滑动刷新令牌的生命周期。 AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true //允许的范围 AllowedScopes = new List<string> { //"UserCenter", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile }, } }; return clients; } /// <summary> /// 定义可以使用ID4的用户资源 /// </summary> /// <returns></returns> public static List<TestUser> GetUsers() { return new List<TestUser>() { new TestUser { SubjectId = "001", Username = "ypf1", //账号 Password = "1", //密码 Claims = { new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), } }, new TestUser { SubjectId = "002", Username = "ypf2", Password = "1", Claims = { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), } } }; } }
Startup 代码
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //注册IDS4信息(授权码模式) services.AddIdentityServer(options=> { options.Authentication.CookieAuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIds()) .AddInMemoryClients(Config.GetClients()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryApiScopes(Config.GetApiScopes()) .AddTestUsers(Config.GetUsers()); services .AddAuthentication(options=> {
//默认为Cookie 同时支持Jwt options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { // IdentityServer4 地址 options.Authority = "http://localhost:7071"; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = false,//是否验证Issuer ValidateAudience = false,//是否验证Audience ValidateLifetime = true,//是否验证失效时间 ValidateIssuerSigningKey = false,//是否验证SecurityKey //ValidAudience = "UserCenter",//Audience //ValidIssuer = "yourdomain.com",//Issuer,这两项和前面签发jwt的设置一致 //ValidAudiences = new List<string>() { "client2" }//, "api1" }; }); } // 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"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); //启用IDS4 app.UseIdentityServer(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
api 使用jwt验证
[Route("api/[controller]/[action]")] [ApiController] [Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]//(JwtBearerDefaults.AuthenticationScheme) public class DemoController : ControllerBase { [Authorize] public JsonResult Index() { //string str = OpenIdConnectParameterNames.RefreshToken; string accessToken = HttpContext.GetTokenAsync("access_token").Result; string idToken = HttpContext.GetTokenAsync("id_token").Result; var refreshToken = HttpContext.GetTokenAsync("refresh_token").Result; var claimsList = from c in User.Claims select new { c.Type, c.Value }; return new JsonResult(new { data = new { accessToken, idToken, refreshToken, claimsList } }); } }
同时服务端需要下载IdentityServer4.Quickstart.UI 代码 (github上有)
以下3个文件复制到你的服务端项目中
客户端
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) { services.AddControllersWithViews(); JwtSecurityTokenHandler.DefaultMapInboundClaims = false; JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //添加Cookie认证 services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") //通过OIDC协议远程请求认证 .AddOpenIdConnect("oidc", options => { options.Authority = "http://localhost:7071"; //认证授权服务器地址 #region 授权码模式 //options.RequireHttpsMetadata = false; //options.ClientId = "client1"; //客户端ID //options.ClientSecret = "123456"; //客户端秘钥 ////授权码模式 //options.ResponseType = OpenIdConnectResponseType.Code; //options.ResponseMode = OpenIdConnectResponseMode.Query; //options.SaveTokens = true; #endregion #region 混合客户端模式 options.RequireHttpsMetadata = false; options.ClientId = "client2"; //客户端ID options.ClientSecret = "123456"; //客户端秘钥 //混合客户端模式 options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken; //options.ResponseMode = OpenIdConnectResponseMode.Query; options.SaveTokens = true; #endregion }); } // 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"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); //开启认证 app.UseAuthentication(); //开启授权 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } }
客户端api增加授权控制,在没有授权的情况下访问,客户端会跳转到服务端登录进行认证,认证通过会转回到客户端
这里服务端认证中心可以提供获取用户信息接口,客户端可以使用拿到的token请求认证中心的用户信息接口