自定义的AuthorizeAttribute在ASP.NET Core中

huangapple go评论59阅读模式
英文:

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&lt;AuthorizationGrpcServices&gt;() ??
throw new ArgumentNullException(nameof(AuthorizationGrpcServices));
var tokenCache = context.HttpContext.RequestServices.GetService&lt;ICacheRepository&gt;() ??
throw new ArgumentNullException(nameof(ICacheRepository));
if (!context.HttpContext.Request.Headers.TryGetValue(&quot;Authorization&quot;, out var authorizationHeader))
{
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
return;
}
var jwtToken = authorizationHeader.ToString().Replace(&quot;Bearer &quot;, &quot;&quot;);
ValidateTokenResponse? response;
// try to get token and roles from cache
response = await tokenCache.GetAsync&lt;ValidateTokenResponse&gt;(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(&quot;,&quot;);
var requiredRoleList = requiredRoles.Split(&quot;|&quot;);
foreach (var requiredRoleGroup in requiredRoleList)
{
var requiredRoleSubList = requiredRoleGroup.Split(&quot;,&quot;);
var hasRequiredRole = true;
foreach (var requiredRole in requiredRoleSubList)
{
if (!roleList.Any(x =&gt; 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(&quot;User&quot;)] // required user role
[AuthorizeByIdentityServer(&quot;User,Admin&quot;)]// required user role and admin role
[AuthorizeByIdentityServer(&quot;User|Admin&quot;)] // required user role or admin role

huangapple
  • 本文由 发表于 2023年2月19日 15:55:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75498723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定