使用 IAuthorizationRequirementData 自定义授权策略

使用 IAuthorizationRequirementData 接口在属性定义中指定与授权策略关联的要求。

示例应用

本文中所述的完整示例是 AuthRequirementsData 示例应用(GitHub 存储库)(dotnet/AspNetCore.Docs.Samples如何下载)。 该示例应用为用户实现最低年龄处理程序,要求用户提供出生日期声明,指示他们至少为 21 岁。

最小年龄授权属性

IAuthorizationRequirementDataMinimumAgeAuthorizeAttribute 实现设置授权页面:

using Microsoft.AspNetCore.Authorization;

namespace AuthRequirementsData.Authorization;

class MinimumAgeAuthorizeAttribute(int age) : AuthorizeAttribute, 
    IAuthorizationRequirement, IAuthorizationRequirementData
{
    public int Age { get; set; } = age;

    public IEnumerable<IAuthorizationRequirement> GetRequirements()
    {
        yield return this;
    }
}

最小年龄授权处理程序

MinimumAgeAuthorizationHandler 类处理 MinimumAgeAuthorizeAttribute 提供的单个 IAuthorizationRequirement,这通过泛型参数 MinimumAgeAuthorizeAttribute 来指定。

HandleRequirementAsync 方法:

  • 获取用户的出生日期声明。
  • 从声明获取用户的年龄。
  • 如果用户今年还没有过生日,则调整年龄。
  • 如果用户满足年龄要求,则标记授权要求成功。
  • 实现日志记录以实现演示目的。
using System.Globalization;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;

namespace AuthRequirementsData.Authorization;

class MinimumAgeAuthorizationHandler(ILogger<MinimumAgeAuthorizationHandler> logger) 
    : AuthorizationHandler<MinimumAgeAuthorizeAttribute>
{
    // Check whether a given minimum age requirement is satisfied.
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, 
        MinimumAgeAuthorizeAttribute requirement)
    {
        logger.LogInformation(
            "Evaluating authorization requirement for age >= {age}", 
            requirement.Age);

        // Get the user's birth date claim.
        var dateOfBirthClaim = 
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth);

        if (dateOfBirthClaim != null)
        {
            // If the user has a date of birth claim, obtain their age.
            var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value, 
                CultureInfo.InvariantCulture);
            var age = DateTime.Now.Year - dateOfBirth.Year;

            // Adjust age if the user hasn't had a birthday yet this year.
            if (dateOfBirth > DateTime.Now.AddYears(-age))
            {
                age--;
            }

            // If the user meets the age requirement, mark the authorization
            // requirement succeeded.
            if (age >= requirement.Age)
            {
                logger.LogInformation(
                    "Minimum age authorization requirement {age} satisfied", 
                    requirement.Age);
                context.Succeed(requirement);
            }
            else
            {
                logger.LogInformation(
                    "Current user's DateOfBirth claim ({dateOfBirth}) doesn't " +
                    "satisfy the minimum age authorization requirement {age}",
                    dateOfBirthClaim.Value,
                    requirement.Age);
            }
        }
        else
        {
            logger.LogInformation("No DateOfBirth claim present");
        }

        return Task.CompletedTask;
    }
}

MinimumAgeAuthorizationHandler作为单例IAuthorizationHandler服务在应用的Program文件中注册。

builder.Services.AddSingleton<IAuthorizationHandler,
    MinimumAgeAuthorizationHandler>();

在用户满足最低年龄政策的情况下,通过使用年龄为 21 岁的GreetingsController属性,[MinimumAgeAuthorize({AGE})]将显示用户的姓名,其中{AGE}占位符代表年龄。

using Microsoft.AspNetCore.Mvc;
using AuthRequirementsData.Authorization;

namespace AuthRequirementsData.Controllers;

[ApiController]
[Route("api/[controller]")]
public class GreetingsController : Controller
{
    [MinimumAgeAuthorize(21)]
    [HttpGet("hello")]
    public string Hello() => 
        $"Hello {HttpContext.User.Identity?.Name}!";
}

如果用户的出生日期声明指示他们至少为 21 岁,控制器将显示问候字符串,发出 200 (OK) 状态代码。 如果用户缺少出生日期声明或声明指示用户未满 21 岁,那么问候语将不会显示,并且系统会返回 403(禁止)状态代码。

JWT 持有者身份验证服务将添加到应用的 Program 文件中:

builder.Services.AddAuthentication().AddJwtBearer();

应用设置文件 (appsettings.json) 为 JWT 持有者身份验证配置受众和颁发者:

"Authentication": {
  "Schemes": {
    "Bearer": {
      "ValidAudiences": [
        "https://localhost:51100"
      ],
      "ValidIssuer": "dotnet-user-jwts"
    }
  }
}

在前面的示例中,localhost 受众与应用启动配置文件(applicationUrl)中指定的 localhost 地址Properties/launchSettings.json匹配。

演示

使用 dotnet user-jwts 和 curl 测试示例。

在命令行界面中的项目文件夹中,执行以下命令创建一个 JWT 持有者令牌,该令牌的出生日期声明使用户超过 21 岁:

dotnet user-jwts create --claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth=1989-01-01

输出在命令 shell 中的“Token:”后面生成令牌:

New JWT saved with ID '{JWT ID}'.
Name: {USER}
Custom Claims: [http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth=1989-01-01]

Token: {TOKEN}

把标记的值(前面的输出中出现的 {TOKEN} 占位符)存起来,以便以后使用。

你可以在联机 JWT 解码器中解码令牌,例如 jwt.ms,以查看其内容,并显示其中包含一个带有用户出生日期的 birthdate 声明:

{
  "alg": "HS256",
  "typ": "JWT"
}.{
  "unique_name": "{USER}",
  "sub": "{USER}",
  "jti": "{JWT ID}",
  "birthdate": "1989-01-01",
  "aud": [
    "https://localhost:51100",
    "http://localhost:51101"
  ],
  "nbf": 1747315312,
  "exp": 1755264112,
  "iat": 1747315313,
  "iss": "dotnet-user-jwts"
}.[Signature]

使用一个 dateofbirth 值再次执行该命令,该值使用户年龄低于 21 岁:

dotnet user-jwts create --claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth=2020-01-01

将第二个令牌的值暂存起来。

在 Visual Studio 中或使用 dotnet watch .NET CLI 中的命令启动应用。

在 .NET CLI 中,执行以下命令 curl.exe 来请求 api/greetings/hello 终结点。 将 {TOKEN} 占位符替换为之前保存的第一个 JWT 持有者令牌:

curl.exe -i -H "Authorization: Bearer {TOKEN}" https://localhost:51100/api/greetings/hello

输出指示成功,因为用户的出生日期声明指示他们至少为 21 岁:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Thu, 15 May 2025 22:58:10 GMT
Server: Kestrel
Transfer-Encoding: chunked

Hello {USER}!

日志记录指明满足了年龄要求:

MinimumAgeAuthorizationHandler: Information: Evaluating authorization requirement for age >= 21
MinimumAgeAuthorizationHandler: Information: Minimum age authorization requirement 21 satisfied

使用第二个令牌重新执行 curl.exe 命令,指示用户年龄低于 21 岁。 输出指示不满足要求。 禁止访问终结点(状态代码 403):

HTTP/1.1 403 Forbidden
Content-Length: 0
Date: Thu, 15 May 2025 22:58:36 GMT
Server: Kestrel

MinimumAgeAuthorizationHandler: Information: Evaluating authorization requirement for age >= 21
MinimumAgeAuthorizationHandler: Information: Current user's DateOfBirth claim (2020-01-01) doesn't satisfy the minimum age authorization requirement 21