簡介
本人做微服務項目也有一段時間了,在微服務中讓我感觸頗深的就是這個IdentityServer4了,ID4(IdentityServer4的簡稱)中涉及的概念頗多,本文不談概念(我怕讀者沒耐心看下去),在這分享下我個人的使用心得。
目錄
1. ID4簡介
2. ID4使用
ID4簡介
相信大家都知道面向對象中的封裝(把通用的功能封裝起來,減少程序中大量重復代碼),我們知道在一個單體系統中有很多的重復模塊,例如:身份認證、權限控制檢查等,在單體系統中,這些都可以使用aop統一在一個地方控制。而在分布式系統,每個系統都需要進行身份認證、權限檢查等。這時,每個系統都得寫一套同樣的代碼來進行這些控制,我們能不能像單體系統那樣在一個地方進行這些流程呢?這時我們可以使用IdentityServer4來實現。
IdentityServer4是一個集成 身份認證和授權 的組件,使用OpenId Connect(身份識別框架) 和 Auth2.0(授權框架)來進行身份認證和授權的。
ID4使用
我這里只列出幾個主要的類,其它,可以下載項目來看。關於如何使用,代碼有點多,我比較懶,就沒怎么講解,感興趣的小伙伴可以加個QQ:1983702356 來討論下。
-
項目地址: https://github.com/MapleWithoutWords/IdentityServer4Demo
(注意:下載項目后,記得把數據庫鏈接字符串改下) -
項目環境:
- .net core 2.2
- IdentityServer 2.5
- Mysql 8
-
項目結構:
WEBAPPLICATION1 (IDENTITYSERVER4)項目,安裝 INSTALL-PACKAGE IDENTITYSERVER4 -VERSION 2.5.0,
-
以下幾個類比較關鍵
-
Config.cs。主要是獲取身份資源
1
2
3
4
5
6
7
8
9
10
11
12public class Config
{
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(), //必須要添加,否則報無效的 scope 錯誤
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
} -
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//這個賬號支持訪問哪些應用
};
}
} -
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
};
}
} -
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") //模擬獲取登錄用戶的權限信息
});
}
} -
ProfileService.cs,用戶信息
1
2
3
4
5
6
7
8
9
10
11
12
13public 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;
}
}
-
- Startup類
1 |
public class Startup |
- 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控制台項目, 生成數據庫,添加幾條數據
運行效果
-
在 WebApplication1 目錄下運行cmd命令 dotnet run ,啟動IdentitServer4,端口是9500
-
運行項目 WebApplication3 ,端口是5000
-
當我們直接調用 WebApplication3 API中的方法時,發現返回狀態碼為 401
-
請求Id4,復制返回的 access_token
-
再請求WebApplication3 API,並在報文頭帶上token
結束語
個人認為我這種使用方式和其它使用方式最大的好處就是,可以寫一個IdentityServer4的Client、APIResource增刪改查,然后因為每次請求的時候都是從數據庫讀取數據的,如果數據被修改了,會立即生效。