NETCORE - IdentityServer4 密码认证
NETCORE - IdentityServer4 密码认证
官网:http://www.identityserver.com.cn/Home/Detail/shiyongpingzhengbaohuapi
ABP中身份认证可参考 :https://www.cnblogs.com/1285026182YUAN/p/15190387.html
Ocelot中的身份认证可参考:https://www.cnblogs.com/1285026182YUAN/p/15402841.html
单点登录参考:https://www.cnblogs.com/1285026182YUAN/p/15251708.html
从数据库账户中验证:https://zhuanlan.zhihu.com/p/112506129
https://blog.csdn.net/xiaoxionglove/article/details/114991779
鉴权独立部署:https://www.jb51.net/article/183505.htm
IdentityServer4是基于openid+auth2.0基础实现的一整套完善的授权中心
1. OAuth 2.0是有关如何颁发访问令牌的规范,提供Access Token用于访问服务接口的 2. OpenID Connect是有关如何发行ID令牌的规范,提供Id Token用于用户身份标识,Id Token是基于JWT格式
案例环境:vs2019 , net core , webapi , .net5
1. 创建项目:NETCORE.IdentityServer4.Service
增加 Nuget 包,identityServer4
2. 新增 Config.cs 类
public class Config { //下载ids4的依赖 //scopes define the resources in your system public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; } public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("API1", "第一个接口") { //重要 Scopes = { "scope1" } } }; } public static IEnumerable<ApiScope> GetApiScopes() { return new List<ApiScope> { new ApiScope("scope1") }; } public static IEnumerable<TestUser> Users() { return new[] { new TestUser { SubjectId="1", Username="mail@qq.com", Password="password" } }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "app", ClientSecrets={new Secret("secret".Sha256())},
AccessTokenLifetime = 60,//设置 token 有效期 60秒。(过期时间默认偏移5分钟)
AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "scope1" } } }; } }
3. 修改 startup.cs ,注入 IdentityService4
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "NETCORE.IdentityServer4.Service", Version = "v1" }); }); services.AddIdentityServer()//Ids4服务 .AddDeveloperSigningCredential()//添加开发人员签名凭据 .AddTestUsers(Config.Users().ToList()) .AddInMemoryIdentityResources(Config.GetIdentityResources())//添加内存apiresource .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryApiScopes(Config.GetApiScopes()) .AddInMemoryClients(Config.GetClients());//把配置文件的client配置资源放到内存 }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NETCORE.IdentityServer4.Service v1")); } app.UseHttpsRedirection(); app.UseRouting(); //启动ids4中间件 app.UseIdentityServer();
app.UseAuthentication();//添加鉴权验证
app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
4. 启动项目
此时授权服务中心已经运行,可以访问以下接口获取得到ids4默认discovery endpoint(定义了一个服务发现的规范),它定义了一个api( /.well-known/openid-configuration ),这个api返回一个json数据结构,其中包含了一些OIDC中提供的服务以及其支持情况的描述信息,这样可以使得oidc服务的RP可以不再硬编码OIDC服务接口信息。
访问 https://localhost:5001/.well-known/openid-configuration
可得到如下数据:
{ "issuer": "http://localhost:5000", //发行网址,也就是说我们的权限验证站点 "jwks_uri": "http://localhost:5000/.well-known/openid-configuration/jwks", // 这个接口获取的是公钥,用于验证jwt的数字签名部分(数字签名由sso维护的私钥生成)用的。 "authorization_endpoint": "http://localhost:5000/connect/authorize", // 授权服务器的授权端点的URL "token_endpoint": "http://localhost:5000/connect/token", // 获取token的URL接口 "userinfo_endpoint": "http://localhost:5000/connect/userinfo", // 根据token获取用户信息 "end_session_endpoint": "http://localhost:5000/connect/endsession", // 登录注销 "check_session_iframe": "http://localhost:5000/connect/checksession", // 客户端对check_session_iframe执行监视,可以获取用户的登出状态 "revocation_endpoint": "http://localhost:5000/connect/revocation", // 这个网址允许撤销访问令牌(仅access tokens 和reference tokens)。它实现了令牌撤销规范(RFC 7009) "introspection_endpoint": "http://localhost:5000/connect/introspect", // introspection_endpoint是RFC 7662的实现。 它可以用于验证reference tokens(或如果消费者不支持适当的JWT或加密库,则JWTs) "device_authorization_endpoint": "http://localhost:5000/connect/deviceauthorization", // "frontchannel_logout_supported": true, // 可选。基于前端的注销机制 "frontchannel_logout_session_supported": true, // 可选。基于session的注销机制 "backchannel_logout_supported": true, // 指示OP支持后端通道注销 "backchannel_logout_session_supported": true, // 可选的。指定RP是否需要在注销令牌中包含sid(session ID)声明,以在使用backchannel_logout_uri时用OP标识RP会话。如果省略,默认值为false "scopes_supported": [ "openid", "profile", "scope1", "offline_access" ], // 支持的范围 "claims_supported": [ "sub", "name", "family_name", "given_name", "middle_name", "nickname", "preferred_username", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale", "updated_at" ], // 支持的claims "grant_types_supported": [ "authorization_code", "client_credentials", "refresh_token", "implicit", "password", "urn:ietf:params:oauth:grant-type:device_code" ], // 授权类型 "response_types_supported": [ "code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token" ], // 支持的请求方式 "response_modes_supported": [ "form_post", "query", "fragment" ], // 传值方式 "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post" ], // JSON数组,包含此令牌端点支持的客户端身份验证方法列表 "id_token_signing_alg_values_supported": [ "RS256" ], "subject_types_supported": [ "public" ], // JSON数组,包含此OP支持的主题标识符类型列表。 有效值是 pairwise 和 public.类型。 更多信息 "code_challenge_methods_supported": [ "plain", "S256" ], // JSON数组,包含此授权服务器支持的PKCE代码方法列表 "request_parameter_supported": true }
5. 获取token
由于之前配置的GrantTypes.ResourceOwnerPasswordAndClientCredentials模式,同时支持password和client_credentials两种模式访问
1) 使用password模式授权,注意使用 x-www-form-urlencoded 方式
地址:https://localhost:5001/connect/token
参数:
grant_type | password |
client_id | app |
client_secret | secret |
username | mail@qq.com |
password | password |
2) 使用 client_credentials 模式授权,注意使用 x-www-form-urlencoded 方式
地址:https://localhost:5001/connect/token
参数:
grant_type | password |
client_id | app |
client_secret | secret |
从数据库账户中获取数据认证
1. 新增 ResourceOwnerPasswordValidator.cs 类
using IdentityServer4.Validation; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using System; using IdentityModel; using IdentityServer4.Models; using Newtonsoft.Json; namespace NETCORE.IdentityServer4.Service { public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { public ResourceOwnerPasswordValidator() { } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { //根据context.UserName和context.Password与数据库的数据做校验,判断是否合法 if (context.UserName == "wjk" && context.Password == "123") { context.Result = new GrantValidationResult( subject: context.UserName, authenticationMethod: "custom", //authenticationMethod: OidcConstants.AuthenticationMethods.Password, claims: GetUserClaims()); //context.Result = new GrantValidationResult( // JsonConvert.SerializeObject(context.UserName), // OidcConstants.AuthenticationMethods.UserPresenceTest); } else { //验证失败 context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential"); } } //可以根据需要设置相应的Claim private Claim[] GetUserClaims() { return new Claim[] { new Claim("userid", 1.ToString()), new Claim(JwtClaimTypes.Name,"wjk"), new Claim(JwtClaimTypes.GivenName, "ssss"), new Claim(JwtClaimTypes.FamilyName, "ddddd"), new Claim(JwtClaimTypes.Email, "11111@qq.com"), new Claim(JwtClaimTypes.Role,"admin") }; } } }
2. 在startup中,注入时使用 AddResourceOwnerValidator ,去掉 AddTestUsers
services.AddIdentityServer()//Ids4服务 .AddDeveloperSigningCredential()//添加开发人员签名凭据 //.AddTestUsers(Config.Users().ToList()) .AddInMemoryIdentityResources(Config.GetIdentityResources())//添加内存apiresource .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryApiScopes(Config.GetApiScopes()) .AddInMemoryClients(Config.GetClients())//把配置文件的client配置资源放到内存 .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
3. 测试
接口鉴权
增加 Nuget 包: IdentityServer4.AccessToken.Validation
修改 startup.cs
增加鉴权管理
services.AddAuthentication("Bearer") //.AddIdentityServerAuthentication(options => // { // options.Authority = "http://localhost:5000"; // options.RequireHttpsMetadata = false; // options.ApiName = "MyApi"; // }) .AddJwtBearer("Bearer", options => { options.Authority = "https://localhost:5001"; options.RequireHttpsMetadata = true; // htpps就设置为true options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false };
// token 默认容忍5分钟过期时间偏移,这里设置为0,
//这里就是为什么定义客户端设置了过期时间为5秒,过期后仍可以访问数据
options.TokenValidationParameters.ClockSkew = TimeSpan.Zero;
}); //API授权 services.AddAuthorization(options => { //策略 options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope", "MyApi"); }); }); services.AddControllers();
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NETCORE.IdentityServer4.Service v1")); } app.UseHttpsRedirection(); app.UseRouting(); //启动ids4中间件 app.UseIdentityServer(); app.UseAuthentication();//添加鉴权验证 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
增加控制器:ValuesController.cs
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; namespace NETCORE.IdentityServer4.Service.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { [HttpGet] [Route("GetValue")] public IActionResult GetValue() => Ok("test ok!"); } }
增加鉴权管理 ,属性标签
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; namespace NETCORE.IdentityServer4.Service.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { [HttpGet] [Route("GetValue")] [Authorize] public IActionResult GetValue() => Ok("test ok!"); [HttpGet] [Route("test")] public IActionResult test() => Ok("test test!"); } }
未 使用 token 时,返回 401
使用token时,返回正确的数据
刷新token
获取到新的 token 与 refresh_token
{ "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2MjM0RDczREY3MDkxN0MxOTY2QTczMzM3NjVBNUY3IiwidHlwIjoiSldUIn0.eyJuYmYiOjE2NjU1NDUyNzEsImV4cCI6MTY2NTU0NTU3MSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImF1ZCI6ImFwcCIsImlhdCI6MTY2NTU0NTI3MSwiYXRfaGFzaCI6ImFiVV9lX3dMODN2aXdNVVBkNHRSUXciLCJzdWIiOiJ3amsiLCJhdXRoX3RpbWUiOjE2NjU1NDUwNTQsImlkcCI6ImxvY2FsIiwiYW1yIjpbImN1c3RvbSJdfQ.etyyf9zbSLVksUT0NotT4PB_IGBte4cEQinb6UsNAv1uE5baqcTD-Z1OqtbE4aiytiz0ceCRSKzNHyXNfAJV5KV--RXm0pEXfOAfj0xAbB5pZ0by2zlCUpqo3iEaxyTGAKdyJ3JbwTDAyQfmX9a7PMC2wB_omf0v0S90y2pzHbHRZHoO3XpCM-G7UEJws6lY7Lq7die22AlvlXzQz9hgdL3oIVdJWwbpdaVT7zIKBXTn4Ns75fAUWr9XK-jN35WjlwCJpqgDQ3T1pjpiAZPD1-blUbRootuUu3rNzTn5K8Ns840NZrBNKxr7wNv0p2bWlAu_Cb803jIyASqea8Ddhw", "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2MjM0RDczREY3MDkxN0MxOTY2QTczMzM3NjVBNUY3IiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NjU1NDUyNzEsImV4cCI6MTY2NTU0NTMzMSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImF1ZCI6IkFQSTEiLCJjbGllbnRfaWQiOiJhcHAiLCJzdWIiOiJ3amsiLCJhdXRoX3RpbWUiOjE2NjU1NDUwNTQsImlkcCI6ImxvY2FsIiwianRpIjoiMjI4NUU0NEQxMEMyNkU4NUJGNkRDQzEyQjAyN0YyNjIiLCJpYXQiOjE2NjU1NDUwNTQsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJzY29wZTEiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsiY3VzdG9tIl19.J_M_zXEViDypF81IEBf9ZlDcxdM_RbeKrl9Aq4CKpLdO3nujHwwod-lHgqO4UmOqNGuoVT8JSPR1Y4v1HtaLylOZwbrsp8TqKV_Z5YximMywlXf4D8y1UG3Gb3KHA8KZLnKNC1F2AJT1Bd3WVDs_nu1jbv77H81Hj4TRJkwYQf0AYSUGo8vSx6MhHZutYO_ExrX7rWtZutByu85gj9ctc5V6t8fVjgVMmZZaWJaVFJtENcGY1TTGnRkAUD-HxlxcYOJ6F0Xgu2V91EJLtxzrH5Uoc-2cWVFy8t1h2vopFIiCOAOyZ7WLyIDNoKyLmYI-_dNpTCOhlCSPeVUzpar7yw", "expires_in": 60, "token_type": "Bearer", "refresh_token": "F1149C80C86F46E37A11C99735ABB5B6A4C52212718CE0E13D355FE7CC221824", "scope": "openid profile scope1 offline_access" }
其他接口
https://localhost:7015/connect/userinfo
参考: https://blog.csdn.net/ml344739968/article/details/110640458
项目:NETCORE.IdentityServer4.ervice
附代码:https://gitee.com/wuxincaicai/NETCORE.git