IdentityServer4身份验证(IS4服务与受保护的API资源在一个项目中)
阴阳法眼
2020年03月10日 22:27
收录于文集
共13篇

本文主要介绍在Asp.net core 中 使用 IdentityServer4 的客户端凭证、客户端凭证+用户+密码两种验证模式。

在个人开发的小型项目中,往往并不需要单独建立IS4授权服务器,而是将 IS4 服务与 要提供客户端访问的API资源放在一个项目中。 此外,本文展示了在IS4验证用户时,如何从数据库中读取用户。

本文在 asp.net core 3.1  + WPF客户端,环境下测试通过。

1、导入程序集

*IS4 服务端* IdentityServer4 IdentityServer4.AspNetIdentity Microsoft.AspNetCore.Authentication.JwtBearer *客户端,只需下面一个* IdentityModel

2、创建IS4配置类IS4Config.cs

     /// <summary>      /// IdentityServer4的配置类      /// </summary>      public class IS4Config      {          /// <summary>          /// 获取IS4自身内置的标准API资源          /// </summary>          /// <returns></returns>          public static IEnumerable<IdentityResource> GetIdentityResources()          {              return new IdentityResource[]              {                  new IdentityResources.OpenId(),                  new IdentityResources.Profile(),              };          }          /// <summary>          /// 获取应用需调用的(用户自己编写的)API资源          /// </summary>          /// <returns></returns>          public static IEnumerable<ApiResource> GetApiResources()          {              return new ApiResource[] { new ApiResource("Apis",&#​34;My Apis") };          }          /// <summary>          /// 定义将访问IS4 的客户端          /// </summary>          /// <returns></returns>          public static IEnumerable<Client> GetClients()          {              return new Client[] {                  new Client(){                      //定义客户端,支持两种验证模式                      ClientId="theClient&#​34;,                      AllowedGrantTypes={                          GrantType.ClientCredentials,  //客户端模式                          GrantType.ResourceOwnerPassword //密码模式                      },                      ClientSecrets={new Secret("theClientPwd&#​34;.Sha256())},                      AllowedScopes={ "Apis",                      IdentityServerConstants.StandardScopes.Profile,                      IdentityServerConstants.StandardScopes.OpenId,                      IdentityServerConstants.StandardScopes.OfflineAccess                      }                  },              };          }      }

3、创建IS4 用户查询类ResourceOwnerPasswordValidator.cs

 public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator      {          private readonly DataUserRepository _UserRepository; //用户数据表操作对象          public ResourceOwnerPasswordValidator(GHDbContext ghDbContet)          {              _UserRepository = new DataUserRepository(ghDbContet);          }          public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)          {              try              {                  var user = await _UserRepository.GetOneByIdPwdAsync(context.UserName, context.Password);                  //如用户名与密码都正确,User 才不会为 null                  if (user != null)                  {                      context.Result = new GrantValidationResult(                              subject: user.Id.ToString(),                              authenticationMethod: "custom&#​34;,                              claims: GetUserClaims(user) //视情况,可以不需要此claims                              );                      return;                  }                  else                  {                        context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "无效的用户名或密码&#​34;);                      return;                  }              }              catch (Exception ex)              {                  context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, ex.Message);              }          }            //build claims array from user data            public static Claim[] GetUserClaims(ModelUser user)          {              return new Claim[]              {                  new Claim("user_id&#​34;, user.Id ?? "&#​34;),                  new Claim(JwtClaimTypes.Name,user.Id ?? "&#​34;),                  new Claim(JwtClaimTypes.Role, user.Post)              };          }      }

4、修改setup.cs类

  public void ConfigureServices(IServiceCollection services)          {              //注册IS4 服务              var is4Buider = services.AddIdentityServer()                    .AddDeveloperSigningCredential()                    .AddInMemoryApiResources(IS4Config.GetApiResources()) //IS4 导入定义的应用资源API                    .AddInMemoryIdentityResources(IS4Config.GetIdentityResources())  //IS4 自身API                    .AddInMemoryClients(IS4Config.GetClients())  //IS4 导入定义的客户端                    .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();//IS4 从数据表中验证用户类                                            //注册验证(*用于保护API资源,与IS4无关* )              services.AddAuthentication("Bearer&#​34;).AddJwtBearer(r =>              {                  //认证服务地址                  r.Authority = "http://localhost:5000&#​34;;                  //权限标识                  r.Audience = "Apis";                  //是否必需HTTPS                  r.RequireHttpsMetadata = false;              });          }             public void Configure(IApplicationBuilder app, IWebHostEnvironment env)          {              app.UseRouting();              //使用 IS4 中间件。              app.UseIdentityServer();                            //添加验证(*用于保护API资源,与IS4无关* )              app.UseAuthentication(); //注意:验证中间件必须放在授权之前。**              //添加授权(*用于保护API资源,与IS4无关* )              app.UseAuthorization();//              //上两行要放在app.UseRouting、app.UseCors("cors&#​34;)之后,并且在app.UseEndpoints之前             app.UseEndpoints(endpoints =>              {                  endpoints.MapControllers();              });          }

5、欲进行验证的客户端代码(比如 WPF客户端、控制台应用等)

  /// <summary>          /// 获取AccessToaken          /// </summary>          /// <returns></returns>          public static async Task GetAccessToken(string P_UserName = null, string P_Pwd = null)          {              IS4_AccessToken = "&#​34;;              HttpClient _Client = new HttpClient();              var disco = await _Client.GetDiscoveryDocumentAsync("http://localhost:5000&#​34;);              //发现 IS4 各类功能所在的网址和其他相关信息              if (disco.IsError)              {                  Console.WriteLine(disco.Error);                  return;              }                ////ClientCredentials 客户端凭据许可              //var tokenResponse = await _Client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest              //{              //    Address = disco.TokenEndpoint, //令牌端点 = http://localhost:5000/connect/token              //    ClientId = "WorkPlatformClient&#​34;,    //id              //    ClientSecret = "Work_Platform_ClientPwd&#​34;, //pwd              //    Scope = "Apis&#​34;,  //请求的api              //});                //ClientPassword 客户端加用户密码模式              var tokenResponse = await _Client.RequestPasswordTokenAsync(new PasswordTokenRequest              {                  Address = disco.TokenEndpoint,                  ClientId = "theClient&#​34;,                    ClientSecret = "theClientPwd&#​34;,                  Scope = "Apis",                                      //以下为用户与密码                  UserName = P_UserName,                  Password = P_Pwd              });              if (tokenResponse.IsError)              {                  Console.WriteLine(tokenResponse.Error);                  return;              }              //JObject TokenJsonObj = tokenResponse.Json;              if (tokenResponse.AccessToken != null)              {                  IS4_AccessToken = tokenResponse.AccessToken;              }                //// 调用 指定的API              //var Apiclient = new HttpClient();              //Apiclient.SetBearerToken(tokenResponse.AccessToken);                //var response = await Apiclient.GetAsync("http://localhost:5001/identity");              //if (!response.IsSuccessStatusCode)              //{              //    Console.WriteLine(response.StatusCode);              //}              //else              //{              //    var content = await response.Content.ReadAsStringAsync();              //    Console.WriteLine(JArray.Parse(content));              //}          }