IdentityServer4 - AuthorizeAttribute 不验证 JWT 令牌/自定义用户存储
Posted
技术标签:
【中文标题】IdentityServer4 - AuthorizeAttribute 不验证 JWT 令牌/自定义用户存储【英文标题】:IdentityServer4 - AuthorizeAttribute doesn't validate the JWT token / Custom user store 【发布时间】:2021-10-07 06:47:04 【问题描述】:我有 Angular 12 前端和 ASP.NET Core 5 后端。后端使用自定义用户存储实现了 IdentityServer4,因为以后可以轻松地将其替换为 Active Directory。
问题是AuthorizeAttribute
([Authorize]
) 不起作用。它只是一直显示 401 未经授权。我认为services.AddAuthentication
有问题,因为它负责令牌的验证。
我还希望能够使用角色[Authorize(Roles = Role.Administrator,Role.DepartmentAdministrator)]
。我相信如果令牌开始工作 RoleClaimType = "role"
会使其工作,但我不能确定,直到我先修复令牌验证。
片段
public static class InfrastructureServicesExtensions
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration)
...
services.AddIdentityServer(options =>
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
)
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Configuration.GetIdentityResources())
.AddInMemoryApiScopes(Configuration.GetApiScopes())
.AddInMemoryApiResources(Configuration.GetApiResources(configuration))
.AddInMemoryClients(Configuration.GetClients(configuration))
.AddCustomUserStore();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
//.AddJwtBearer(options =>
//
// options.RequireHttpsMetadata = false;
// options.SaveToken = true;
// options.TokenValidationParameters = new TokenValidationParameters
//
// ValidateIssuerSigningKey = true,
// IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["AuthConfiguration:ClientSecret"])),
// ValidateIssuer = false,
// ValidateAudience = false
// ;
//);
.AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, jwtOptions =>
jwtOptions.Authority = "http://localhost:5000";
jwtOptions.RequireHttpsMetadata = false;
,
referenceOptions =>
referenceOptions.Authority = "http://localhost:5000";
referenceOptions.RoleClaimType = "role";
referenceOptions.ClientId = configuration["AuthConfiguration:ClientId"];
referenceOptions.ClientSecret = configuration["AuthConfiguration:ClientSecret"];
);
return services;
[Authorize]
public class RoomsController : ApiControllerBase
[HttpGet]
public async Task<ActionResult<IList<RoomDto>>> GetRooms()
var result = await Mediator.Send(new GetRoomsQuery()).ConfigureAwait(false);
return Ok(result);
[HttpPost]
public async Task<ActionResult<int>> Create(CreateRoomCommand command)
return await Mediator.Send(command).ConfigureAwait(false);
[HttpPut("id:int")]
public async Task<ActionResult> Update(int id, UpdateRoomCommand command)
if (id != command.Id)
return BadRequest();
await Mediator.Send(command).ConfigureAwait(false);
return NoContent();
[HttpDelete("id:int")]
public async Task<ActionResult> Delete(int id)
await Mediator.Send(new DeleteRoomCommand Id = id ).ConfigureAwait(false);
return NoContent();
public static class CustomIdentityServerBuilderExtensions
public static IIdentityServerBuilder AddCustomUserStore(this IIdentityServerBuilder builder)
builder.Services.AddSingleton<IUserRepository, UserRepository>();
builder
.AddProfileService<CustomProfileService>()
.AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>();
return builder;
public class CustomProfileService : IProfileService
private readonly IUserRepository _userRepository;
public CustomProfileService(IUserRepository userRepository)
_userRepository = userRepository;
public Task GetProfileDataAsync(ProfileDataRequestContext context)
var sub = context.Subject.GetSubjectId();
var user = _userRepository.FindById(sub);
var claims = new List<Claim>
new("username", user.Username),
new("email", user.Email),
new("role", user.Role.ToDescriptionString())
;
context.IssuedClaims = claims;
return Task.CompletedTask;
public Task IsActiveAsync(IsActiveContext context)
var sub = context.Subject.GetSubjectId();
var user = _userRepository.FindById(sub);
context.IsActive = user != null;
return Task.CompletedTask;
public class UserRepository : IUserRepository
private readonly List<User> _users = new()
new User
Id = "1",
Username = "admin",
Password = "123456",
Email = "admin@uni-ruse.bg",
Role = Role.Administrator
,
new User
Id = "2",
Username = "katadmin",
Password = "123456",
Email = "katadmin@uni-ruse.bg",
Role = Role.DepartmentAdministrator
,
new User
Id = "3",
Username = "user",
Password = "123456",
Email = "user@uni-ruse.bg",
Role = Role.User
;
public bool ValidateCredentials(string username, string password)
var user = FindByUsername(username);
return user != null && user.Password.Equals(password);
public User FindById(string id)
return _users.FirstOrDefault(x => x.Id == id);
public User FindByUsername(string username)
return _users.FirstOrDefault(x => x.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
private readonly IUserRepository _userRepository;
public CustomResourceOwnerPasswordValidator(IUserRepository userRepository)
_userRepository = userRepository;
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
if (_userRepository.ValidateCredentials(context.UserName, context.Password))
var user = _userRepository.FindByUsername(context.UserName);
context.Result = new GrantValidationResult(user.Id, OidcConstants.AuthenticationMethods.Password);
return Task.CompletedTask;
public enum Role
[Description("Администратор")]
Administrator,
[Description("Катедрен администратор")]
DepartmentAdministrator,
[Description("Потребител")]
User
public class User
public string Id get; set;
public string Email get; set;
public string Username get; set;
public string Password get; set;
public Role Role get; set;
public static class Configuration
public static IEnumerable<IdentityResource> GetIdentityResources() =>
new List<IdentityResource>
new IdentityResources.OpenId(),
new IdentityResources.Profile()
;
public static IEnumerable<ApiScope> GetApiScopes() =>
new List<ApiScope>
new("assapi", "Academic Schedule API")
;
public static IEnumerable<ApiResource> GetApiResources(IConfiguration configuration) =>
new List<ApiResource>
new("assapi", "Academic Schedule API")
ApiSecrets = new List<Secret>
new(configuration["AuthConfiguration:ClientSecret"].Sha256())
,
Scopes =
"assapi"
;
public static IEnumerable<Client> GetClients(IConfiguration configuration) =>
new List<Client>
new()
ClientName = configuration["AuthConfiguration:ClientName"],
ClientId = configuration["AuthConfiguration:ClientId"],
ClientSecrets = new Secret(configuration["AuthConfiguration:ClientSecret"].Sha256()) ,
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AccessTokenType = AccessTokenType.Jwt,
AllowOfflineAccess = true,
AccessTokenLifetime = 120,
IdentityTokenLifetime = 120,
UpdateAccessTokenClaimsOnRefresh = true,
SlidingRefreshTokenLifetime = 300,
RefreshTokenExpiration = TokenExpiration.Absolute,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
AlwaysSendClientClaims = true,
AllowedScopes =
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"assapi"
;
public void Configure(IApplicationBuilder app)
if (Environment.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseCors("CorsPolicy");
app.UseSwagger();
app.UseSwaggerUI(c =>
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Academic Schedule API V1");
);
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapDefaultControllerRoute();
);
【问题讨论】:
【参考方案1】:问题解决了。我忘记了当我将它作为控制台应用程序启动时可以看到日志。它告诉我发行者存在问题,实际上它确实与配置类中的问题不匹配。现在它们确实匹配并且令牌工作正常。
[Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Roles = "Administrator")]
角色也在起作用。
RoleClaimType = "role"
【讨论】:
以上是关于IdentityServer4 - AuthorizeAttribute 不验证 JWT 令牌/自定义用户存储的主要内容,如果未能解决你的问题,请参考以下文章