我试图使一个自定义授权属性在ASP。净的核心。在以前的版本中,可以重写bool AuthorizeCore(HttpContextBase httpContext)。但是这在AuthorizeAttribute中不再存在。

当前制作自定义AuthorizeAttribute的方法是什么?

我想要完成的:我正在头授权中接收会话ID。通过该ID,我将知道特定操作是否有效。


当前回答

我有不记名令牌,我可以阅读索赔。 我在控制器和动作上使用该属性

public class CustomAuthorizationAttribute : ActionFilterAttribute
{
    public string[] Claims;

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // check user 
        var contextUser = context?.HttpContext?.User;
        if (contextUser == null)
        {
            throw new BusinessException("Forbidden");
        }


        // check roles
        var roles = contextUser.FindAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/role").Select(c => c.Value).ToList();
        if (!roles.Any(s => Claims.Contains(s)))
        {
            throw new BusinessException("Forbidden");
        }

        base.OnActionExecuting(context);
    }
}

例子

[CustomAuthorization(Claims = new string[]
    {
        nameof(AuthorizationRole.HR_ADMIN),
        nameof(AuthorizationRole.HR_SETTING)
    })]
[Route("api/[controller]")]
[ApiController]
public class SomeAdminController : ControllerBase
{
    private readonly IMediator _mediator;

    public SomeAdminController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpGet("list/SomeList")]
    public async Task<IActionResult> SomeList()
        => Ok(await _mediator.Send(new SomeListQuery()));
}

这就是角色

public struct AuthorizationRole
{
    public static string HR_ADMIN;
    public static string HR_SETTING;
}

其他回答

现代的方法是AuthenticationHandlers

在startup.cs中添加

services.AddAuthentication("BasicAuthentication").AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private readonly IUserService _userService;

        public BasicAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IUserService userService)
            : base(options, logger, encoder, clock)
        {
            _userService = userService;
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey("Authorization"))
                return AuthenticateResult.Fail("Missing Authorization Header");

            User user = null;
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
                var username = credentials[0];
                var password = credentials[1];
                user = await _userService.Authenticate(username, password);
            }
            catch
            {
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

            if (user == null)
                return AuthenticateResult.Fail("Invalid User-name or Password");

            var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);
        }
    }

IUserService是一个你有用户名和密码的服务。 基本上它返回一个用户类,您可以使用它来映射您的声明。

var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
            }; 

然后你可以查询这些索赔和她的任何数据你映射,有相当多,看看ClaimTypes类

您可以在扩展方法中使用它来获取任何映射

public int? GetUserId()
{
   if (context.User.Identity.IsAuthenticated)
    {
       var id=context.User.FindFirst(ClaimTypes.NameIdentifier);
       if (!(id is null) && int.TryParse(id.Value, out var userId))
            return userId;
     }
      return new Nullable<int>();
 }

我认为这种新方法比这里展示的旧方法更好,两种方法都有效

public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (actionContext.Request.Headers.Authorization != null)
        {
            var authToken = actionContext.Request.Headers.Authorization.Parameter;
            // decoding authToken we get decode value in 'Username:Password' format
            var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
            // spliting decodeauthToken using ':'
            var arrUserNameandPassword = decodeauthToken.Split(':');
            // at 0th postion of array we get username and at 1st we get password
            if (IsAuthorizedUser(arrUserNameandPassword[0], arrUserNameandPassword[1]))
            {
                // setting current principle
                Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(arrUserNameandPassword[0]), null);
            }
            else
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
            }
        }
        else
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
        }
    }

    public static bool IsAuthorizedUser(string Username, string Password)
    {
        // In this method we can handle our database logic here...
        return Username.Equals("test") && Password == "test";
    }
}

似乎ASP。NET Core 2,你可以继承AuthorizeAttribute,你只需要实现IAuthorizationFilter(或IAsyncAuthorizationFilter):

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly string _someFilterParameter;

    public CustomAuthorizeAttribute(string someFilterParameter)
    {
        _someFilterParameter = someFilterParameter;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;

        if (!user.Identity.IsAuthenticated)
        {
            // it isn't needed to set unauthorized result 
            // as the base class already requires the user to be authenticated
            // this also makes redirect to a login page work properly
            // context.Result = new UnauthorizedResult();
            return;
        }

        // you can also use registered services
        var someService = context.HttpContext.RequestServices.GetService<ISomeService>();

        var isAuthorized = someService.IsUserAuthorized(user.Identity.Name, _someFilterParameter);
        if (!isAuthorized)
        {
            context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
            return;
        }
    }
}

什么? !

我决定再补充一个简单的答案。B/c我发现大多数答案都有点过度设计。也因为我需要一种授予授权的方法,而不仅仅是否认它。这里的大多数答案都提供了一种“加强”安全性的方法,但我想“放松”它。例如:“如果配置了某些应用程序设置,则允许匿名用户访问”。

public class MyAuthAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        //check access 
        if (CheckPermissions())
        {
            //all good, add optional code if you want. Or don't
        }
        else
        {
            //DENIED!
            //return "ChallengeResult" to redirect to login page (for example)
            context.Result = new ChallengeResult(CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

就是这样。不需要混淆“策略”,“声明”,“处理程序”和其他[哔]

用法:

// GET api/Get/5
[MyAuth]
public ActionResult<string> Get(int id)
{
    return "blahblah";
}

我有不记名令牌,我可以阅读索赔。 我在控制器和动作上使用该属性

public class CustomAuthorizationAttribute : ActionFilterAttribute
{
    public string[] Claims;

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // check user 
        var contextUser = context?.HttpContext?.User;
        if (contextUser == null)
        {
            throw new BusinessException("Forbidden");
        }


        // check roles
        var roles = contextUser.FindAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/role").Select(c => c.Value).ToList();
        if (!roles.Any(s => Claims.Contains(s)))
        {
            throw new BusinessException("Forbidden");
        }

        base.OnActionExecuting(context);
    }
}

例子

[CustomAuthorization(Claims = new string[]
    {
        nameof(AuthorizationRole.HR_ADMIN),
        nameof(AuthorizationRole.HR_SETTING)
    })]
[Route("api/[controller]")]
[ApiController]
public class SomeAdminController : ControllerBase
{
    private readonly IMediator _mediator;

    public SomeAdminController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpGet("list/SomeList")]
    public async Task<IActionResult> SomeList()
        => Ok(await _mediator.Send(new SomeListQuery()));
}

这就是角色

public struct AuthorizationRole
{
    public static string HR_ADMIN;
    public static string HR_SETTING;
}

如果有人只是想在授权阶段使用当前的安全实践验证一个承载令牌,

将此添加到Startup/ConfigureServices中

    services.AddSingleton<IAuthorizationHandler, BearerAuthorizationHandler>();
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();

    services.AddAuthorization(options => options.AddPolicy("Bearer",
        policy => policy.AddRequirements(new BearerRequirement())
        )
    );

在你的代码库中,

public class BearerRequirement : IAuthorizationRequirement
{
    public async Task<bool> IsTokenValid(SomeValidationContext context, string token)
    {
        // here you can check if the token received is valid 
        return true;
    }
}

public class BearerAuthorizationHandler : AuthorizationHandler<BearerRequirement> 
{

    public BearerAuthorizationHandler(SomeValidationContext thatYouCanInject)
    {
       ...
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BearerRequirement requirement)
    {
        var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
        string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"];
        if (authHeader != null && authHeader.Contains("Bearer"))
        {
            var token = authHeader.Replace("Bearer ", string.Empty);
            if (await requirement.IsTokenValid(thatYouCanInject, token))
            {
                context.Succeed(requirement);
            }
        }
    }
}

如果代码没有到达context.Succeed(…),那么无论如何都会失败(401)。

然后在你的控制器中你可以使用

 [Authorize(Policy = "Bearer", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]