IdentityServer4的最佳使用


簡介

  本人做微服務項目也有一段時間了,在微服務中讓我感觸頗深的就是這個IdentityServer4了,ID4(IdentityServer4的簡稱)中涉及的概念頗多,本文不談概念(我怕讀者沒耐心看下去),在這分享下我個人的使用心得。

目錄

  1. ID4簡介

  2. ID4使用

ID4簡介

  相信大家都知道面向對象中的封裝(把通用的功能封裝起來,減少程序中大量重復代碼),我們知道在一個單體系統中有很多的重復模塊,例如:身份認證、權限控制檢查等,在單體系統中,這些都可以使用aop統一在一個地方控制。而在分布式系統,每個系統都需要進行身份認證、權限檢查等。這時,每個系統都得寫一套同樣的代碼來進行這些控制,我們能不能像單體系統那樣在一個地方進行這些流程呢?這時我們可以使用IdentityServer4來實現。

  IdentityServer4是一個集成 身份認證和授權 的組件,使用OpenId Connect(身份識別框架) 和 Auth2.0(授權框架)來進行身份認證和授權的。

ID4使用

我這里只列出幾個主要的類,其它,可以下載項目來看。關於如何使用,代碼有點多,我比較懶,就沒怎么講解,感興趣的小伙伴可以加個QQ:1983702356 來討論下。

WEBAPPLICATION1 (IDENTITYSERVER4)項目,安裝 INSTALL-PACKAGE IDENTITYSERVER4 -VERSION 2.5.0,

  1. 以下幾個類比較關鍵

    1. Config.cs。主要是獲取身份資源

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class Config
      {
      public static IEnumerable<IdentityResource> GetIdentityResources()
      {
      return new List<IdentityResource>
      {
      new IdentityResources.OpenId(), //必須要添加,否則報無效的 scope 錯誤
      new IdentityResources.Profile(),
      new IdentityResources.Email()
      };
      }
      }
    2. TestClientStore.cs 加載IdentityServer4的client

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      /// <summary>
      /// 加載IdentityServer4的client
      /// </summary>
      public class TestClientStore : IClientStore
      {
      /// <summary>
      /// Service層中的一個接口
      /// </summary>
      public IClientService ClientSvc { get; set; }


      public TestClientStore(IClientService ClientSvc)
      {
      this.ClientSvc = ClientSvc;
      }
      public async Task<Client> FindClientByIdAsync(string clientId)
      {
      var dto = await ClientSvc.FindClientByIdAsync(clientId);
      if (dto==null)
      {
      return null;
      }
      var scopes = dto.APIResources.Select(e => e.Name).ToList();
      scopes.Add(IdentityServerConstants.StandardScopes.OpenId);
      scopes.Add(IdentityServerConstants.StandardScopes.Profile);
      return new Client
      {
      ClientId = dto.Client.Id,//API賬號、客戶端Id
      AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
      ClientSecrets =
      {
      new Secret(dto.Client.Secret.Sha256())//秘鑰
      },
      AllowedScopes = scopes//這個賬號支持訪問哪些應用
      };
      }
      }
    3. TestReourceStore.cs,加載IdentityServer的APIResource

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      /// <summary>
      /// 加載IdentityServer的APIResource
      /// </summary>
      public class TestReourceStore : IResourceStore
      {
      public IApiResourceService resourceSvc { get; set; }
      public TestReourceStore(IApiResourceService resourceSvc)
      {
      this.resourceSvc = resourceSvc;
      }
      public async Task<ApiResource> FindApiResourceAsync(string name)
      {
      var entity = await resourceSvc.GetByNameAsync(name);
      return new ApiResource(entity.Name,entity.DisplayName);
      }

      public async Task<IEnumerable<ApiResource>> FindApiResourcesByScopeAsync(IEnumerable<string> scopeNames)
      {
      var list = await resourceSvc.GetDatasByNamesAsync(scopeNames);
      return list.Select(e=>new ApiResource(e.Name,e.DisplayName));
      }

      public async Task<IEnumerable<IdentityResource>> FindIdentityResourcesByScopeAsync(IEnumerable<string> scopeNames)
      {
      return Config.GetIdentityResources().Where(e => scopeNames.Contains(e.Name)).ToList();
      }

      public async Task<Resources> GetAllResourcesAsync()
      {
      var list = await resourceSvc.GetNoramlAll();

      var resouces = list.Select(e => new ApiResource(e.Name, e.DisplayName)).ToList();
      return new Resources
      {
      ApiResources = resouces
      };

      }
      }
    4. TestResourceOwnerPasswordValidator.cs,IdentityServer4登錄驗證

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      /// <summary>
      /// IdentityServer4登錄驗證
      /// </summary>
      public class TestResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
      {
      public IUserService UserSvc { get; set; }
      public TestResourceOwnerPasswordValidator(IUserService UserSvc)
      {
      this.UserSvc = UserSvc;
      }
      public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
      {
      string account = context.UserName;
      string pwd = context.Password;

      var loginResult = await UserSvc.Login(account, pwd);

      if (loginResult == null)
      {
      context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
      return;
      }
      context.Result = new GrantValidationResult(
      subject: context.UserName,
      authenticationMethod: "custom",
      claims: new Claim[] {
      new Claim("Name",context.UserName),
      new Claim("UserId",loginResult.Id),
      new Claim("Roles","Admin,Contact"), //模擬獲取登錄用戶的角色信息
      new Claim("Premissions","List,Delete") //模擬獲取登錄用戶的權限信息
      });

      }
      }
    5. ProfileService.cs,用戶信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class ProfileService : IProfileService
      {
      public async Task GetProfileDataAsync(ProfileDataRequestContext context)
      {
      var claims = context.Subject.Claims.ToList();
      context.IssuedClaims = claims.ToList();
      }

      public async Task IsActiveAsync(IsActiveContext context)
      {
      context.IsActive = true;
      }
      }
  1. Startup類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});

//services.AddAuthentication("Bearer")
// .AddIdentityServerAuthentication(options =>
// {
// options.Authority = "http://localhost:9500";//identity server 地址
// options.RequireHttpsMetadata = false;
// });

string conStr = Configuration["connectionString"];
services.AddDbContext<TestDbContext>(options =>
{
options.UseMySql(conStr);
});


///依賴注入Service層
AddSigletons(services);

#region ID4服務配置
var id4Build = services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources());
//加載apiresource
id4Build.Services.AddTransient<IResourceStore, TestReourceStore>();
//加載client
id4Build.Services.AddTransient<IClientStore, TestClientStore>();
//登錄驗證
id4Build.Services.AddTransient<IResourceOwnerPasswordValidator, TestResourceOwnerPasswordValidator>();
//加載profile。profile是用戶信息
id4Build.Services.AddTransient<IProfileService, ProfileService>();
#endregion


//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);


}

public void AddSigletons(IServiceCollection services)
{
var assem = Assembly.Load("Test.Service");
var list = assem.GetTypes().Where(e => e.IsAbstract == false && typeof(ISignFac).IsAssignableFrom(e));
foreach (var instanType in list)
{
foreach (var item in instanType.GetInterfaces())
{
services.AddSingleton(item, instanType);
}
}
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
//插入id4中間件
app.UseIdentityServer();
//app.UseAuthentication();
//app.UseStaticFiles();
//app.UseCookiePolicy();
//app.UseMvc(routes =>
//{
// routes.MapRoute(
// name: "default",
// template: "{controller=Home}/{action=Index}/{id?}");
//});


}
}
  1. WebApplication3 API項目,修改Startup,並添加一個控制器,在方法上打上一個標簽
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56

    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.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(options =>
    {
    options.Authority = "http://localhost:9500";//identity server 地址
    options.RequireHttpsMetadata = false;
    });

    string conStr = Configuration["connectionString"];
    services.AddDbContext<TestDbContext>(options =>
    {
    options.UseMySql(conStr);
    });


    ///依賴注入Service層
    AddSigletons(services);
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void AddSigletons(IServiceCollection services)
    {
    var assem = Assembly.Load("Test.Service");
    var list = assem.GetTypes().Where(e => e.IsAbstract == false && typeof(ISignFac).IsAssignableFrom(e));
    foreach (var instanType in list)
    {
    foreach (var item in instanType.GetInterfaces())
    {
    services.AddSingleton(item, instanType);
    }
    }
    }
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    if (env.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }
    app.UseAuthentication();

    app.UseMvc();
    }
    }

控制器

運行CONSOLEAPP1控制台項目, 生成數據庫,添加幾條數據

運行ConsoleApp1控制台項目

運行效果

  1. 在 WebApplication1 目錄下運行cmd命令 dotnet run ,啟動IdentitServer4,端口是9500

  2. 運行項目 WebApplication3 ,端口是5000

  3. 當我們直接調用 WebApplication3 API中的方法時,發現返回狀態碼為 401

  4. 請求Id4,復制返回的 access_token

  5. 再請求WebApplication3 API,並在報文頭帶上token

結束語

  個人認為我這種使用方式和其它使用方式最大的好處就是,可以寫一個IdentityServer4的Client、APIResource增刪改查,然后因為每次請求的時候都是從數據庫讀取數據的,如果數據被修改了,會立即生效。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM