什么是IdentitySever4 

什么是identityServer4,这是一个OpenID Connect提供程序的身份验证组件。是一套身份验证框架

服务端将需要限制访问的数据,进行封装套一个壳。只有用户通过令牌验证核对才可以请求服务端包裹壳内的数据。


identity4的认证授权方式
1)客户端认证控制API
2)密码认证方式控制API
3)添加自定义的认证方式


1.identityServer服务端Demo 
1)首先创建一个应用程序

test4j身份证_API

2)引用nuget包 

test4j身份证_identityServer_02

3)在管道中注入和使用identityserver 服务

3.1)首先需要创建一个类。用于注入方法需要提供的信息。都是根据identity自带的 根据你的验证方式自行选择)
 

/// <summary>
    /// 配置的信息。为了在管道中提供相应的方法。
    /// </summary>
    public class Config
    {
        /// <summary>
        /// 这个方法是来描述和阐述,提供外部选择的作用域
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("api1", "My API")
            };
        }

        /// <summary>
        /// 作用域的名称和验证规则
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "client",
                    //授权方式为客户端授权,类型可参考GrantTypes枚举
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    //授权密钥,客户端和服务端事先约定的一个Key
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },                     
                    //允许客户端访问的Scopes[作用域]
                    AllowedScopes = { "api1" }
                }
            };
        }

        /// <summary>
        /// 这个方法是来规范tooken生成的规则和方法的。一般不进行设置,直接采用默认的即可。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new IdentityResource[]
            {
                new IdentityResources.OpenId()
            };
        }

        /// <summary>
        /// 针对用户密码验证方式需要比对的账户
        /// </summary>
        /// <returns></returns>
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>()
            {
                new TestUser()
                {
                    SubjectId="1",
                    Username="YCZ",
                    Password="123456",
                    ProviderName="",        //获取或设置提供程序名称。
                    ProviderSubjectId="",   //获取或设置提供程序主题标识符。
                    IsActive=true,          //获取或设置用户是否处于活动状态。
                    Claims=new List<Claim>()     
 //身份  这是一个List 里面放的是包含的身份。 身份的概念就类似于(一个人是教师/父亲这种标识性的东西可以支持多个)
                  }
                
            };
        }
    }

2)在管道的中注入identityserver4 服务

//管道的前端。注入
public void ConfigureServices(IServiceCollection services)
{
  //在注入的过程的中,同时还会在在内存中注册一个基于内存存储的运行时状态。存储的具体持久化的时间。取决于以下选择哪种持久化的方式。
    var builder = services.AddIdentityServer()
    .AddDeveloperSigningCredential()   
    //扩展在每次启动时,为令牌签名创建了一个临时密钥。在生成环境需要一个持久化的密钥
    .AddInMemoryClients(Config.GetClients())             //验证方式
    .AddInMemoryApiResources(Config.GetApiResources())   
    .AddInMemoryIdentityResources(Config.GetIdentityResources())     //创建接口返回格式
    .AddTestUsers(Config.GetUsers());                    //使用用户密码验证方式
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
 }

3)在管道的末端使用服务

//管道的末端。使用
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDeveloperExceptionPage();
   }
   else
   {
      app.UseHsts();
   }

    app.UseIdentityServer(); //启用identity服务

    app.UseHttpsRedirection();
    app.UseMvc();
}

配置完这些了服务端的配置基本完成了。测试下是否可以使用了起服务在端口号后加上 /.well-known/openid-configuration

如果配置成功会出现如下图的信息。里面是identity的元信息

test4j身份证_服务端_03

4)在首次启动后,会自动生成一个

test4j身份证_作用域_04

文件,是自动创建的开发人员的密匙。


2. API服务端  Demo (需要包裹请求的数据)
1)新起一个服务。 

[HttpGet("Test")]
 [Authorize]   //启用验证
 public string Test(int id)
 {
     return "Hello IdentityServer4";
 }

 // DELETE api/values/5
 [HttpGet("Test2")]
 public string Test2(int id)
 {
    return "Hello IdentityServer4";
 }

在未添加identity时可以正常请求

test4j身份证_API_05

2) 注入验证方式到管道

在ConfigureServices中注入

//将身份验证服务添加到管道中
 services.AddAuthentication("Bearer")
 .AddJwtBearer("Bearer", options =>
 {
     options.Authority = "http://localhost:5001";   //你要请求验证的identity服务端的地址
     options.RequireHttpsMetadata = false;
     options.Audience = "api1";          //你选择的验证方式。 对应的GetClients中定义的作用域
 });

在Configure中使用

app.UseAuthentication(); //使用验证方式 将身份认证中间件添加到管道中,因此将在每次调用API时自动执行身份验证。

再打上验证特性。可以打在控制器上/方法上

[Authorize]   //启用验证

再请求接口显示:

test4j身份证_服务端_06

test4j身份证_服务端_07

启用验证的接口已经无法访问了。没启用的还可以正常访问


3. 创建 客户端 Demo 

// GET api/values
 [HttpGet("CilentGetTest")]
public async Task<string> CilentGetTest()
{
    #region  模拟客户端请求api接口
    var client = new HttpClient();
    //var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001/connect/token");  //要请求的验证的地址
    //模拟客户端验证
    var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest   
            {
                Address ="http://localhost:5001/connect/token",    //identity自带的获取验证令牌的路由

                ClientId = "client",       //对应server端定义的配置
                ClientSecret = "secret",
                Scope = "api1"
            });

            if (tokenResponse.IsError)
            {
                return tokenResponse.Error;
            }

            client.SetBearerToken(tokenResponse.AccessToken);  //要将Token发送到API,通常使用HTTP Authorization标头。 这是使用SetBearerToken扩展方法完成的:

            var response = await client.GetAsync("http://localhost:5050/api/values/Test");  //需要请求接口/controller的地址
            if (!response.IsSuccessStatusCode)
            {
                return response.StatusCode;
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            #endregion

            return "";

        }

先去identity服务端根据验证规则获取令牌。再拿着令牌去请求接口进行比对。(你请求的验证方式和你接口保存的验证方式必须一致)

获取的令牌样式为:

test4j身份证_作用域_08

请求成功:

test4j身份证_作用域_09

验证方式作用域错误会是:

test4j身份证_test4j身份证_10

验证方式不匹配(拿着api1去端口验证api2):

返回值为空


4.创建用户密码请求Demo

在identityserver的服务端的

在GetApiResources方法中添加一个新的作用域
 

new ApiResource("api2", "My API")

GetClients方法中添加一种用户密码的验证方式

new Client
{
         ClientId = "client2",
         AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,  //账户密码验证方式

         ClientSecrets =
         {
               new Secret("secret2".Sha256())
         },
         AllowedScopes = { "api2" }
 }

修改接口端的验证方式

test4j身份证_API_11

接口中创建api请求。

/// <summary>
        /// 模拟用户密码验证
        /// </summary>
        /// <returns></returns>
        [HttpGet("CilentGetTest2")]
        public async Task<string> CilentGetTest2()
        {
            var client =new HttpClient();
            var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
            {
                Address = "http://localhost:5001/connect/token",
                ClientId = "client2",
                ClientSecret = "secret2",
                Scope = "api2",
                UserName = "YCZ",
                Password = "123456",
            });

            if (tokenResponse.IsError)
            {
                return tokenResponse.Error;
            }

            client.SetBearerToken(tokenResponse.AccessToken);  //要将Token发送到API,通常使用HTTP Authorization标头。 这是使用SetBearerToken扩展方法完成的:

            var response = await client.GetAsync("http://localhost:5050/api/values/Test");  //需要请求接口/controller的地址
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            return "";
        }

配置成功后。请求:

test4j身份证_作用域_12


建议:如果报错信息为作用域错误。可以在identityserver端。进行元数据的查看。如果元数据配置的信息有能显示出来再去检查接口端和请求端的代码。