繼上一篇的 客戶授權模式,這篇會繼續實現 密碼授權模式以及 刷新 AccessToken 的方式
密碼授權模式
與 客戶授權模式 的差異在參數增加了 username 以及 password,在取得token時一併驗證 username 及 password,並且在 JWT 中添加自定義的資訊,比如說 Roles
username / password 檢查
實務上會實際存取資料庫,驗證並返回相關會員的資料,為了方便這邊僅寫死從程式碼內做判斷及檢查
實作 IResourceOwnerPasswordValidator
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
if (context.Request.Raw["password"] != "password" ||
context.Request.Raw["username"] != "username")
{
return Task.CompletedTask;
}
context.Result.IsError = false;
context.Result.Subject = GetClaimsPrincipal();
return Task.CompletedTask;
}
private ClaimsPrincipal GetClaimsPrincipal()
{
var issued = DateTimeOffset.Now.ToUnixTimeSeconds();
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Subject, Guid.NewGuid().ToString()),
new Claim(JwtClaimTypes.AuthenticationTime, issued.ToString()),
new Claim(JwtClaimTypes.IdentityProvider, "localhost"),
};
return new ClaimsPrincipal(new ClaimsIdentity(claims));
}
}
調整 Startup
加上最後一行,AddResourceOwnerValidator
services.AddIdentityServer()
.AddInMemoryApiResources(IdentityConfig.GetApiResources())
.AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
.AddInMemoryClients(IdentityConfig.GetClients())
.AddInMemoryApiScopes(IdentityConfig.GetScopes())
.AddDeveloperSigningCredential()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
在JWT加入自定義的內容
實作 IProfileService
public class CustomProfileService : IProfileService
{
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
context.IssuedClaims.AddRange(new List<Claim>()
{
new Claim(JwtClaimTypes.Role, "admin")
});
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = true;
return Task.CompletedTask;
}
}
調整 Startup
加上最後一行,CustomProfileService
services.AddIdentityServer()
.AddInMemoryApiResources(IdentityConfig.GetApiResources())
.AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
.AddInMemoryClients(IdentityConfig.GetClients())
.AddInMemoryApiScopes(IdentityConfig.GetScopes())
.AddDeveloperSigningCredential(false)
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
.AddProfileService<CustomProfileService>();
調整 IdentityConfig
將 Client 的 AllowedGrantTypes 更改為 “GrantTypes.ResourceOwnerPassword”
執行
request的參數增加了 username 和 password,而 grant_type 更改為 “password”
密碼錯誤時則無法取得 token
檢查 JWT 可以看到增加了自定義的內容
若 Authrize 有限制 Roles 時,也是會有作用的
Refresh AccessToken
調整 IdentityServerConfig
調整 Client
- 在 Scope 加上 IdentityServerConstants.StandardScopes.OfflineAccess
- 加上 token 相關的參數(有效時間、使用RefreshToken、過期方式…等等)
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256()),
},
AllowedScopes =
{
"MyApi",
IdentityServerConstants.StandardScopes.OfflineAccess
},
AllowOfflineAccess = true,
RefreshTokenUsage = TokenUsage.ReUse,
AccessTokenLifetime = 18000,
RefreshTokenExpiration = TokenExpiration.Absolute,
AbsoluteRefreshTokenLifetime = 300,
},
執行
取得 token 時會一併返回 refreshToken
拿 refreshToken 可以取的新的 AccessToken(注意這邊用的是不同的 grant_type)
花了幾天玩了一下 IdentityServer,筆記一下整個過程,之後有時間再補上連接 SQL Server的做法
SampleCode
IdentityServer4
IdentityServer4 官方文檔用到的Templates
晓晨Master Blog
密碼授權模式
與 客戶授權模式 的差異在參數增加了 username 以及 password,在取得token時一併驗證 username 及 password,並且在 JWT 中添加自定義的資訊,比如說 Roles
username / password 檢查
實務上會實際存取資料庫,驗證並返回相關會員的資料,為了方便這邊僅寫死從程式碼內做判斷及檢查
實作 IResourceOwnerPasswordValidator
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { if (context.Request.Raw["password"] != "password" || context.Request.Raw["username"] != "username") { return Task.CompletedTask; } context.Result.IsError = false; context.Result.Subject = GetClaimsPrincipal(); return Task.CompletedTask; } private ClaimsPrincipal GetClaimsPrincipal() { var issued = DateTimeOffset.Now.ToUnixTimeSeconds(); var claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, Guid.NewGuid().ToString()), new Claim(JwtClaimTypes.AuthenticationTime, issued.ToString()), new Claim(JwtClaimTypes.IdentityProvider, "localhost"), }; return new ClaimsPrincipal(new ClaimsIdentity(claims)); } }
調整 Startup
加上最後一行,AddResourceOwnerValidator
services.AddIdentityServer() .AddInMemoryApiResources(IdentityConfig.GetApiResources()) .AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources()) .AddInMemoryClients(IdentityConfig.GetClients()) .AddInMemoryApiScopes(IdentityConfig.GetScopes()) .AddDeveloperSigningCredential() .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
在JWT加入自定義的內容
實作 IProfileService
public class CustomProfileService : IProfileService { public Task GetProfileDataAsync(ProfileDataRequestContext context) { context.IssuedClaims.AddRange(new List<Claim>() { new Claim(JwtClaimTypes.Role, "admin") }); return Task.CompletedTask; } public Task IsActiveAsync(IsActiveContext context) { context.IsActive = true; return Task.CompletedTask; } }
調整 Startup
加上最後一行,CustomProfileService
services.AddIdentityServer() .AddInMemoryApiResources(IdentityConfig.GetApiResources()) .AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources()) .AddInMemoryClients(IdentityConfig.GetClients()) .AddInMemoryApiScopes(IdentityConfig.GetScopes()) .AddDeveloperSigningCredential(false) .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>() .AddProfileService<CustomProfileService>();
調整 IdentityConfig
將 Client 的 AllowedGrantTypes 更改為 “GrantTypes.ResourceOwnerPassword”
執行
request的參數增加了 username 和 password,而 grant_type 更改為 “password”
密碼錯誤時則無法取得 token
檢查 JWT 可以看到增加了自定義的內容
若 Authrize 有限制 Roles 時,也是會有作用的
Refresh AccessToken
調整 IdentityServerConfig
調整 Client
new Client { ClientId = "client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()), }, AllowedScopes = { "MyApi", IdentityServerConstants.StandardScopes.OfflineAccess }, AllowOfflineAccess = true, RefreshTokenUsage = TokenUsage.ReUse, AccessTokenLifetime = 18000, RefreshTokenExpiration = TokenExpiration.Absolute, AbsoluteRefreshTokenLifetime = 300, },
執行
取得 token 時會一併返回 refreshToken
拿 refreshToken 可以取的新的 AccessToken(注意這邊用的是不同的 grant_type)
花了幾天玩了一下 IdentityServer,筆記一下整個過程,之後有時間再補上連接 SQL Server的做法
SampleCode
IdentityServer4
IdentityServer4 官方文檔用到的Templates
晓晨Master Blog