博客
关于我
Identity Server4 笔记
阅读量:307 次
发布时间:2019-03-01

本文共 7719 字,大约阅读时间需要 25 分钟。

一. 角色

1. 身份认证服务器

身份认证服务器是一个向客户端发送安全令牌的软件。它的主要功能包括:

  • 保护资源:确保只有授权用户能够访问受保护的API。
  • 身份验证:使用本地帐户或外部身份提供商(如微信登录)验证用户身份。
  • 会话管理:提供单点登录功能,管理用户会话。
  • 客户端管理:认证并管理客户端应用程序。
  • 令牌发布:向客户端发布身份令牌(Identity Token)和访问令牌(Access Token)。
  • 令牌验证:验证客户端提交的令牌,确保其合法性。

身份认证服务器主要支持以下协议:

  • OpenID Connect:提供身份验证和单点登录功能。
  • OAuth 2.0:用于客户端认证和资源访问。

2. 用户

用户是使用客户端的人,通过身份认证服务器获取访问令牌,从而能够访问受保护的API。

3. 客户端

客户端是请求身份认证服务器获取令牌的软件,用于认证用户(获取身份令牌)或访问资源(获取访问令牌)。客户端必须先注册于身份认证服务器,才能请求令牌。常见类型包括:

  • 机密客户端:运行在服务端,例如Web应用程序,使用客户端密钥进行认证。
  • 公开客户端:运行在客户端设备,例如移动应用或原生应用,通常用于隐式流(Implicit Flow)。

4. 资源

资源是被保护的API或身份数据。每个API有一个唯一的名称,客户端使用该名称指定要访问的API。

5. 身份令牌(Identity Token)

身份令牌表示身份认证完成的结果,至少包含用户的身份信息及权限信息。它通常用于验证用户身份。

6. 访问令牌(Access Token)

访问令牌用于获取API访问权限。客户端通过提交访问令牌向受保护API进行认证,令牌包含客户端和使用者的信息。

二. 客户端凭据的认证方式

在没有用户参与的程序中,客户端使用客户端凭据(Client Credentials)进行认证。这种方式不涉及用户交互,完全依赖于程序的配置。

1. 建立 IdentityServer4

配置IdentityServer4的步骤如下:

  • 定义API和客户端:在Config类中定义API和客户端。
  • public static class Config
    {
    public static IEnumerable
    ApiScopes
    {
    return new List
    {
    new ApiScope("api1", "My API")
    };
    }
    public static IEnumerable
    Clients
    {
    return new List
    {
    new Client
    {
    ClientId = "client",
    AllowedGrantTypes = GrantTypes.ClientCredentials,
    ClientSecrets = { new Secret("secret".Sha256()) },
    AllowedScopes = { "api1" }
    }
    };
    }
    }
    1. 配置服务:在Startup类中添加IdentityServer4服务,并配置客户端。
    2. public class Startup : IStartup
      {
      public IConfiguration Configuration { get; set; }
      public Startup(IConfiguration configuration)
      {
      Configuration = configuration;
      }
      public void ConfigureServices(IServiceCollection services)
      {
      services.AddControllers();
      var builder = services.AddIdentityServer()
      .AddInMemoryApiScopes(Config.ApiScopes)
      .AddInMemoryClients(Config.Clients)
      .AddDeveloperSigningCredential();
      }
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
      if (env.IsDevelopment())
      {
      app.UseDeveloperExceptionPage();
      }
      app.UseRouting();
      app.UseAuthentication();
      app.UseAuthorization();
      app.UseEndpoints(endpoints =>
      {
      endpoints.MapControllers();
      });
      }
      }
      1. 运行命令:使用命令行运行IdentityServer4。
      2. dotnet run --urls http://*:5001

        2. 建立受保护的API

      3. 定义受保护的API:在IdentityController中使用[Authorize]属性标记受保护的端点。
      4. [ApiController]
        public class IdentityController : ControllerBase
        {
        [Authorize]
        [HttpGet("identity")]
        public IActionResult Get()
        {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }
        [Authorize]
        [HttpGet("test")]
        public string Getst()
        {
        return "受保护的API访问成功";
        }
        }
        1. 配置服务:在Startup类中添加认证和授权服务。
        2. public void ConfigureServices(IServiceCollection services)
          {
          services.AddControllers();
          services.AddAuthentication("Bearer")
          .AddJwtBearer("Bearer", options =>
          {
          options.Authority = "https://localhost:5001";
          options.TokenValidationParameters = new TokenValidationParameters
          {
          ValidateAudience = false
          };
          });
          services.AddAuthorization(options =>
          {
          options.AddPolicy("ApiScope", policy =>
          {
          policy.RequireAuthenticatedUser();
          policy.RequireClaim("scope", "api1");
          });
          });
          }
          public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
          {
          if (env.IsDevelopment())
          {
          app.UseDeveloperExceptionPage();
          }
          app.UseRouting();
          app.UseAuthentication();
          app.UseAuthorization();
          app.UseEndpoints(endpoints =>
          {
          endpoints.MapControllers();
          });
          }
          1. 客户端获取令牌:使用HttpClient获取令牌。
          2. public class Program
            {
            public static async Task Main(string[] args)
            {
            var client = new HttpClient();
            var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001");
            if (disco.IsError)
            {
            Console.WriteLine($"连接失败:{disco.Error}");
            return;
            }
            var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
            Address = disco.TokenEndpoint,
            ClientId = "client",
            ClientSecret = "secret",
            Scope = "api1"
            });
            if (tokenResponse.IsError)
            {
            Console.WriteLine($"token返回错误:{tokenResponse.Error}");
            return;
            }
            Console.WriteLine(tokenResponse.Json);
            var apiClient = new HttpClient();
            apiClient.SetBearerToken(tokenResponse.AccessToken);
            var response = await apiClient.GetAsync("http://localhost:6001/test");
            if (!response.IsSuccessStatusCode)
            {
            Console.WriteLine(response.StatusCode);
            Console.WriteLine("访问失败");
            }
            else
            {
            var content = await response.Content.ReadAsStringAsync();
            Console.WriteLine("访问成功");
            Console.WriteLine(content);
            }
            Console.ReadLine();
            }
            }

            3. 保护WPF客户端

            WPF客户端涉及用户交互,使用密码grant类型进行认证。

          3. 流程
            • 浏览器将请求传给用户代理。
            • 用户代理连接身份认证服务器,获取授权代码。
            • 用户同意后,浏览器将授权代码提交至身份认证服务器,获取访问令牌。
            • 客户端使用访问令牌访问受保护API。
            1. 刷新令牌:在MVC控制器中添加令牌刷新功能。
            2. private async Task
              RenewTokensAsync()
              {
              var client = new HttpClient();
              var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
              if (disco.IsError)
              {
              throw new Exception(disco.Error);
              }
              var refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
              var tokenResponse = await client.RequestRefreshTokenAsync(new RefreshTokenRequest
              {
              Address = disco.TokenEndpoint,
              ClientId = "mvc client",
              ClientSecret = "mvc secret",
              Scope = "api1 openid profile email phone address",
              GrantType = OpenIdConnectGrantTypes.RefreshToken,
              RefreshToken = refreshToken
              });
              if (tokenResponse.IsError)
              {
              throw new Exception(tokenResponse.Error);
              }
              var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
              var tokens = new[]
              {
              new AuthenticationToken
              {
              Name = OpenIdConnectParameterNames.IdToken,
              Value = tokenResponse.IdentityToken
              },
              new AuthenticationToken
              {
              Name = OpenIdConnectParameterNames.AccessToken,
              Value = tokenResponse.AccessToken
              },
              new AuthenticationToken
              {
              Name = OpenIdConnectParameterNames.RefreshToken,
              Value = tokenResponse.RefreshToken
              },
              new AuthenticationToken
              {
              Name = "expires_at",
              Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
              }
              };
              await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
              currentAuthenticateResult.Properties.StoreTokens(tokens);
              await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, currentAuthenticateResult.Principal, currentAuthenticateResult.Properties);
              return tokenResponse.AccessToken;
              }

              三. 混合流

              混合流结合了隐式流和客户端凭据流的优点,适用于需要获取身份令牌和访问令牌的场景。

            3. 客户端类型
              • 机密客户端:适用于服务端应用,如MVC。
              • 公开客户端:适用于桌面应用、移动应用等。
              1. 返回类型
                • response_type = code Id_token
                • response_type = code token
                • response_type = code id_token token

                四. 角色和策略

              2. 角色
                • 定义多个角色,如管理员和普通用户。
                • 每个角色可以包含多个用户。
                1. 策略
                  • 基于角色:使用[Authorize(Roles = "管理员,普通用户")]限制访问权限。
                  • 基于策略:定义自定义策略,如RequireClaim(JwtClaimTypes.FamilyName, "Smith")

                  五. 使用混合流保护客户端

                  混合流允许客户端在不直接暴露访问令牌的情况下,通过后端通道获取令牌,从而保护敏感信息。

                  参考文献

                  [1] [2] [3] [4]

    转载地址:http://xuwl.baihongyu.com/

    你可能感兴趣的文章
    Node.js卸载超详细步骤(附图文讲解)
    查看>>
    Node.js基于Express框架搭建一个简单的注册登录Web功能
    查看>>
    Node.js安装与配置指南:轻松启航您的JavaScript服务器之旅
    查看>>
    Node.js安装及环境配置之Windows篇
    查看>>
    Node.js安装和入门 - 2行代码让你能够启动一个Server
    查看>>
    node.js安装方法
    查看>>
    Node.js官网无法正常访问时安装NodeJS的方法
    查看>>
    Node.js的循环与异步问题
    查看>>
    Node.js高级编程:用Javascript构建可伸缩应用(1)1.1 介绍和安装-安装Node
    查看>>
    nodejs + socket.io 同时使用http 和 https
    查看>>
    NodeJS @kubernetes/client-node连接到kubernetes集群的方法
    查看>>
    Nodejs express 获取url参数,post参数的三种方式
    查看>>
    nodejs http小爬虫
    查看>>
    nodejs libararies
    查看>>
    nodejs npm常用命令
    查看>>
    Nodejs process.nextTick() 使用详解
    查看>>
    nodejs 发起 GET 请求示例和 POST 请求示例
    查看>>
    NodeJS 导入导出模块的方法( 代码演示 )
    查看>>
    nodejs 开发websocket 笔记
    查看>>
    nodejs 的 Buffer 详解
    查看>>