英文:
custom AuthorizeAttribute in ASP.NET Core
问题
I have an asp.net core identity server with grpc and an asp.net core API, and I want to implement a process where when a user sends a request with a JWT token to the asp.net core API, the JWT token is processed by my custom authorize attribute. Within this custom authorize attribute, I send the JWT token to my identity server via gRPC. The identity server responds with roles and access permission, and I store these roles and the token in a Redis database. This way, if the token is already in the Redis database, there's no need to send a request to the identity server. I'm unsure about how to handle this. Should my attribute use an Authorization Filter or an authorization handler? My gRPC server is ready.
I make a request to my gRPC service using:
AuthorizationGrpcServices.ValidateToken(string token)
The response looks like this:
bool access = true
string roles = "Admin, User"
I need a custom attribute that utilizes Redis and this gRPC service.
英文:
I have asp.net core identity server with grpc and a asp.net core api and i what to when user sends a request with jwt token to the asp.net core api the jwt token goes to my custom authorize attribute and in my custom authorize attribute i send the jwt token to my identity server with grpc and at the response identity server give me roles and has access that is true and after that i store roles and token in the redis database so every time if token was in redis no need to send a request to identity server so i don't know how to Handel it should my attribute use Authorization Filter or authorization handler and my grpc server is ready
i request to my grpc service
AuthorizationGrpcServices.ValidateToken(string token)
and the result is this like this
bool access = true
string roles = " Admin , User "
so i need custom attribute that use redis and this grpc service
答案1
得分: 0
你可以通过自定义属性来实现这个:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizeByIdentityServer : ActionFilterAttribute, IAsyncActionFilter
{
private readonly string? requiredRoles;
public AuthorizeByIdentityServer(string? requiredRoles = null)
{
this.requiredRoles = requiredRoles;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var authorizationGrpcServices =
context.HttpContext.RequestServices.GetService<AuthorizationGrpcServices>() ??
throw new ArgumentNullException(nameof(AuthorizationGrpcServices));
var tokenCache = context.HttpContext.RequestServices.GetService<ICacheRepository>() ??
throw new ArgumentNullException(nameof(ICacheRepository));
if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authorizationHeader))
{
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
return;
}
var jwtToken = authorizationHeader.ToString().Replace("Bearer ", "");
ValidateTokenResponse? response;
// 尝试从缓存中获取令牌和角色
response = await tokenCache.GetAsync<ValidateTokenResponse>(jwtToken);
if (response == null)
{
response = await authorizationGrpcServices.ValidateTokenAsync(jwtToken);
if (response.Valid)
{
await tokenCache.SetAsync(jwtToken, response, TimeSpan.FromHours(3));
}
}
if (!response.Valid)
{
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
return;
}
if (requiredRoles == null)
{
await next();
return;
}
// 检查用户角色是否符合要求的角色
var roleList = response.Roles.Split(",");
var requiredRoleList = requiredRoles.Split("|");
foreach (var requiredRoleGroup in requiredRoleList)
{
var requiredRoleSubList = requiredRoleGroup.Split(",");
var hasRequiredRole = true;
foreach (var requiredRole in requiredRoleSubList)
{
if (!roleList.Any(x => x == requiredRole))
{
hasRequiredRole = false;
break;
}
}
if (hasRequiredRole)
{
await next();
return;
}
}
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
}
你可以像这样使用这个自定义属性:
[AuthorizeByIdentityServer] // 不需要角色
[AuthorizeByIdentityServer("User")] // 需要用户角色
[AuthorizeByIdentityServer("User,Admin")] // 需要用户角色和管理员角色
[AuthorizeByIdentityServer("User|Admin")] // 需要用户角色或管理员角色
英文:
You can do this by custom attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizeByIdentityServer : ActionFilterAttribute, IAsyncActionFilter
{
private readonly string? requiredRoles;
public AuthorizeByIdentityServer(string? requiredRoles = null)
{
this.requiredRoles = requiredRoles;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var authorizationGrpcServices =
context.HttpContext.RequestServices.GetService<AuthorizationGrpcServices>() ??
throw new ArgumentNullException(nameof(AuthorizationGrpcServices));
var tokenCache = context.HttpContext.RequestServices.GetService<ICacheRepository>() ??
throw new ArgumentNullException(nameof(ICacheRepository));
if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authorizationHeader))
{
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
return;
}
var jwtToken = authorizationHeader.ToString().Replace("Bearer ", "");
ValidateTokenResponse? response;
// try to get token and roles from cache
response = await tokenCache.GetAsync<ValidateTokenResponse>(jwtToken);
if (response == null)
{
response = await authorizationGrpcServices.ValidateTokenAsync(jwtToken);
if (response.Valid)
{
await tokenCache.SetAsync(jwtToken, response, TimeSpan.FromHours(3));
}
}
if (!response.Valid)
{
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
return;
}
if (requiredRoles == null)
{
await next();
return;
}
//check user role for required roles
var roleList = response.Roles.Split(",");
var requiredRoleList = requiredRoles.Split("|");
foreach (var requiredRoleGroup in requiredRoleList)
{
var requiredRoleSubList = requiredRoleGroup.Split(",");
var hasRequiredRole = true;
foreach (var requiredRole in requiredRoleSubList)
{
if (!roleList.Any(x => x == requiredRole))
{
hasRequiredRole = false;
break;
}
}
if (hasRequiredRole)
{
await next();
return;
}
}
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
}
and you can use this custom attribute like this
[AuthorizeByIdentityServer] //no need for role
[AuthorizeByIdentityServer("User")] // required user role
[AuthorizeByIdentityServer("User,Admin")]// required user role and admin role
[AuthorizeByIdentityServer("User|Admin")] // required user role or admin role
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论