Обновление - IdentityServer 4 был изменен и заменил IUserService на IResourceOwnerPasswordValidator и IProfileService
Я использовал свой UserRepository для получения всех пользовательских данных из базы данных. Это вводится (DI) в конструкторы и определяется в Startup.cs
. Я также создал следующие классы для сервера идентификации (который также вводится):
Сначала определите ResourceOwnerPasswordValidator.cs
:
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private readonly IUserRepository _userRepository;
public ResourceOwnerPasswordValidator(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
try
{
var user = await _userRepository.FindAsync(context.UserName);
if (user != null)
{
if (user.Password == context.Password) {
context.Result = new GrantValidationResult(
subject: user.UserId.ToString(),
authenticationMethod: "custom",
claims: GetUserClaims(user));
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect password");
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "User does not exist.");
return;
}
catch (Exception ex)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
}
}
public static Claim[] GetUserClaims(User user)
{
return new Claim[]
{
new Claim("user_id", user.UserId.ToString() ?? ""),
new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.Firstname) && !string.IsNullOrEmpty(user.Lastname)) ? (user.Firstname + " " + user.Lastname) : ""),
new Claim(JwtClaimTypes.GivenName, user.Firstname ?? ""),
new Claim(JwtClaimTypes.FamilyName, user.Lastname ?? ""),
new Claim(JwtClaimTypes.Email, user.Email ?? ""),
new Claim("some_claim_you_want_to_see", user.Some_Data_From_User ?? ""),
new Claim(JwtClaimTypes.Role, user.Role)
};
}
И ProfileService.cs
:
public class ProfileService : IProfileService
{
private readonly IUserRepository _userRepository;
public ProfileService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
try
{
if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
{
var user = await _userRepository.FindAsync(context.Subject.Identity.Name);
if (user != null)
{
var claims = GetUserClaims(user);
context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
}
}
else
{
var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub");
if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
{
var user = await _userRepository.FindAsync(long.Parse(userId.Value));
if (user != null)
{
var claims = ResourceOwnerPasswordValidator.GetUserClaims(user);
context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();
}
}
}
}
catch (Exception ex)
{
}
}
public async Task IsActiveAsync(IsActiveContext context)
{
try
{
var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "user_id");
if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)
{
var user = await _userRepository.FindAsync(long.Parse(userId.Value));
if (user != null)
{
if (user.IsActive)
{
context.IsActive = user.IsActive;
}
}
}
}
catch (Exception ex)
{
}
}
}
Затем Startup.cs
я сделал следующее:
public void ConfigureServices(IServiceCollection services)
{
var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "idsrv4test.pfx"), "your_cert_password");
services.AddScoped(_ => new YourDbContext(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IUserRepository, UserRepository>();
services.AddIdentityServer()
.AddSigningCredential(cert)
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddProfileService<ProfileService>();
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
services.AddTransient<IProfileService, ProfileService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseIdentityServer();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:50000/",
ApiSecret = "secret",
ApiName = "my.api.resource",
AutomaticAuthenticate = true,
SupportedTokens = SupportedTokens.Both,
AutomaticChallenge = true,
RequireHttpsMetadata = false
};
app.UseIdentityServerAuthentication(identityServerValidationOptions);
}
Вам также понадобится, Config.cs
который определяет ваших клиентов, api и ресурсы. Вы можете найти здесь пример: https://github.com/IdentityServer/IdentityServer4.Demo/blob/master/src/IdentityServer4Demo/Config.cs
Теперь вы можете вызвать IdentityServer / connect / token
Для получения дополнительной информации, пожалуйста, проверьте документацию: https://media.readthedocs.org/pdf/identityserver4/release/identityserver4.pdf
Старый ответ (это больше не работает для нового IdentityServer4)
Это довольно просто, если вы понимаете поток вещей.
Настройте свой IdentityService следующим образом (в Startup.cs - ConfigureServices()
):
var builder = services.AddIdentityServer(options =>
{
options.SigningCertificate = cert;
});
builder.AddInMemoryClients(Clients.Get());
builder.AddInMemoryScopes(Scopes.Get());
builder.Services.AddTransient<IUserService, UserService>();
services.AddTransient<IUserRepository, UserRepository>();
Затем настройте свой UserService
public class UserService : IUserService
{
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
var user = _userRepository.Find(context.UserName);
if (user.Password == context.Password)
{
context.AuthenticateResult = new AuthenticateResult(
user.UserId.ToString(),
user.Email,
new Claim[]
{
new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname),
new Claim(Constants.ClaimTypes.Email, user.Email),
new Claim(Constants.ClaimTypes.Role, user.Role.ToString()),
new Claim("company", user.Company)
}
);
}
return Task.FromResult(0);
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = _userRepository.Find(context.Subject.Identity.Name);
if (user != null)
{
var claims = new Claim[]
{
new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname),
new Claim(Constants.ClaimTypes.Email, user.Email),
new Claim(Constants.ClaimTypes.Role, user.Role.ToString(), ClaimValueTypes.Integer),
new Claim("company", user.Company)
};
context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type));
}
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
var user = _userRepository.Find(context.Subject.Identity.Name);
return Task.FromResult(user != null);
}
}
В основном путем введения UserService
в builder
(типа IdentityServerBuilder
)Services
позволяет ему вызывать UserService при аутентификации.
Я надеюсь, что это поможет другим, так как мне потребовалось пару часов, чтобы начать работу.
IUserService
насколько я понимаю, IdSvr4 (для ASP.NET Core 1.0) больше не существует. Он был заменен двумя интерфейсами / сервисамиIProfileService
иIResourceOwnerPasswordValidator
.context.IssuedClaims = context.Subject.Claims.ToList();
в GetProfileData, это зависит только от того, нужно ли вам скрыть некоторые заявления от общественности или нужно выполнить некоторую промежуточную логику при просмотре данных профиля.В IdentityServer4.
IUserService
больше не доступен, теперь вам нужно использоватьIResourceOwnerPasswordValidator
для аутентификации и использоватьIProfileService
для получения утверждений.В моем сценарии я использую тип разрешения владельца ресурса, и все, что мне нужно, - это получать заявки пользователей на выполнение авторизации на основе ролей для моих веб-API в соответствии с именем пользователя и паролем. И я предположил, что тема уникальна для каждого пользователя.
Я разместил свои коды ниже, и он может работать нормально; может ли кто-нибудь сказать мне, что есть проблемы с моими кодами?
Зарегистрируйте эти две службы в файле startup.cs.
public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer(); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryScopes(Scopes.Get()); builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>(); builder.Services.AddTransient<IProfileService, ProfileService>(); }
Реализуйте
IResourceOwnerPasswordValidator
интерфейс.public class ResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator { public Task<customgrantvalidationresult> ValidateAsync(string userName, string password, ValidatedTokenRequest request) { // Check The UserName And Password In Database, Return The Subject If Correct, Return Null Otherwise // subject = ...... if (subject == null) { var result = new CustomGrantValidationResult("Username Or Password Incorrect"); return Task.FromResult(result); } else { var result = new CustomGrantValidationResult(subject, "password"); return Task.FromResult(result); } } }
Реализуйте
ProfileService
интерфейс.public class ProfileService : IProfileService { public Task GetProfileDataAsync(ProfileDataRequestContext context) { string subject = context.Subject.Claims.ToList().Find(s => s.Type == "sub").Value; try { // Get Claims From Database, And Use Subject To Find The Related Claims, As A Subject Is An Unique Identity Of User //List<string> claimStringList = ...... if (claimStringList == null) { return Task.FromResult(0); } else { List<Claim> claimList = new List<Claim>(); for (int i = 0; i < claimStringList.Count; i++) { claimList.Add(new Claim("role", claimStringList[i])); } context.IssuedClaims = claimList.Where(x => context.RequestedClaimTypes.Contains(x.Type)); return Task.FromResult(0); } } catch { return Task.FromResult(0); } } public Task IsActiveAsync(IsActiveContext context) { return Task.FromResult(0); } }
источник
"IdentityServer4": "1.3.1"
В IdentityServer4 1.0.0-rc5 недоступны ни IUserService, ни CustomGrantValidationResult.
Теперь вместо возврата CustomGrantValidationResult вам нужно будет установить context.Result.
public class ResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator { private MyUserManager _myUserManager { get; set; } public ResourceOwnerPasswordValidator() { _myUserManager = new MyUserManager(); } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var user = await _myUserManager.FindByNameAsync(context.UserName); if (user != null && await _myUserManager.CheckPasswordAsync(user,context.Password)) { context.Result = new GrantValidationResult( subject: "2", authenticationMethod: "custom", claims: someClaimsList); } else { context.Result = new GrantValidationResult( TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return; }
Проверка пароля владельца ресурса
источник