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

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

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


当前回答

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

将此添加到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)]

其他回答

为了在我们的应用程序中进行授权。我们必须根据在授权属性中传递的参数调用服务。

例如,如果我们想检查登录的医生是否可以查看病人的预约,我们将传递“View_Appointment”到自定义授权属性,并在DB服务中检查该权利,并根据结果进行审查。下面是这个场景的代码:

    public class PatientAuthorizeAttribute : TypeFilterAttribute
    {
    public PatientAuthorizeAttribute(params PatientAccessRights[] right) : base(typeof(AuthFilter)) //PatientAccessRights is an enum
    {
        Arguments = new object[] { right };
    }

    private class AuthFilter : IActionFilter
    {
        PatientAccessRights[] right;

        IAuthService authService;

        public AuthFilter(IAuthService authService, PatientAccessRights[] right)
        {
            this.right = right;
            this.authService = authService;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            var allparameters = context.ActionArguments.Values;
            if (allparameters.Count() == 1)
            {
                var param = allparameters.First();
                if (typeof(IPatientRequest).IsAssignableFrom(param.GetType()))
                {
                    IPatientRequest patientRequestInfo = (IPatientRequest)param;
                    PatientAccessRequest userAccessRequest = new PatientAccessRequest();
                    userAccessRequest.Rights = right;
                    userAccessRequest.MemberID = patientRequestInfo.PatientID;
                    var result = authService.CheckUserPatientAccess(userAccessRequest).Result; //this calls DB service to check from DB
                    if (result.Status == ReturnType.Failure)
                    {
                        //TODO: return apirepsonse
                        context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
                    }
                }
                else
                {
                    throw new AppSystemException("PatientAuthorizeAttribute not supported");
                }
            }
            else
            {
                throw new AppSystemException("PatientAuthorizeAttribute not supported");
            }
        }
    }
}

在API操作中,我们像这样使用它:

    [PatientAuthorize(PatientAccessRights.PATIENT_VIEW_APPOINTMENTS)] //this is enum, we can pass multiple
    [HttpPost]
    public SomeReturnType ViewAppointments()
    {

    }

这里很多人已经说过了,但是有了策略处理程序,你就可以在。net框架中使用旧方法实现的功能而言,你可以走得更远。

我在SO网站上快速写了一个答案:https://stackoverflow.com/a/61963465/7081176 对我来说,在制作了一些类后,它完美地工作了:

EditUserRequirement:

public class EditUserRequirement : IAuthorizationRequirement
{
    public EditUserRequirement()
    {
    }
}

一个抽象处理程序,使我的生活更容易:

public abstract class AbstractRequirementHandler<T> : IAuthorizationHandler
    where T : IAuthorizationRequirement
{
    public async Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();
        foreach (var requirement in pendingRequirements)
        {
            if (requirement is T typedRequirement)
            {
                await HandleRequirementAsync(context, typedRequirement);
            }
        }
    }

    protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, T requirement);
}

抽象处理程序的实现:

public class EditUserRequirementHandler : AbstractRequirementHandler<EditUserRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EditUserRequirement requirement)
    {
        // If the user is owner of the resource, allow it.
        if (IsOwner(context.User, g))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }

    private static bool IsOwner(ClaimsPrincipal user, Guid userIdentifier)
    {
        return user.GetUserIdentifier() == userIdentifier;
    }
}

注册我的处理程序和需求: 服务。AddSingleton < IAuthorizationHandler, EditUserRequirementHandler > ();

        services.AddAuthorization(options =>
        {
            options.AddPolicy(Policies.Policies.EditUser, policy =>
            {
                policy.Requirements.Add(new EditUserRequirement());
            });
        });

然后使用我在Blazor的策略:

<AuthorizeView Policy="@Policies.EditUser" Resource="@id">
    <NotAuthorized>
        <Unauthorized />
    </NotAuthorized>
    <Authorized Context="Auth">
        ...
    </Authorized>
</AuthorizeView>

我希望这对面临这个问题的人有用。

基于Derek Greer伟大的答案,我用枚举来做。

下面是我的代码示例:

public enum PermissionItem
{
    User,
    Product,
    Contact,
    Review,
    Client
}

public enum PermissionAction
{
    Read,
    Create,
}


public class AuthorizeAttribute : TypeFilterAttribute
{
    public AuthorizeAttribute(PermissionItem item, PermissionAction action)
    : base(typeof(AuthorizeActionFilter))
    {
        Arguments = new object[] { item, action };
    }
}

public class AuthorizeActionFilter : IAuthorizationFilter
{
    private readonly PermissionItem _item;
    private readonly PermissionAction _action;
    public AuthorizeActionFilter(PermissionItem item, PermissionAction action)
    {
        _item = item;
        _action = action;
    }
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :)

        if (!isAuthorized)
        {
            context.Result = new ForbidResult();
        }
    }
}

public class UserController : BaseController
{
    private readonly DbContext _context;

    public UserController( DbContext context) :
        base()
    {
        _logger = logger;
    }

    [Authorize(PermissionItem.User, PermissionAction.Read)]
    public async Task<IActionResult> Index()
    {
        return View(await _context.User.ToListAsync());
    }
}

什么? !

我决定再补充一个简单的答案。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";
}

现代的方法是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";
    }
}