jwt实现认证和自定义策略授权

场景

客户端根据用户名和密码访问登录接口获取token,服务端登录接口获取账号和密码进行验证,获取用户的角色,若角色是超级管理员则只能授权访问标记为超级管理员的接口,若角色是管理员则只能授权访问标记为管理员的接口。

实现JWT认证

安装JWT包
Microsoft.AspNetCore.Authentication.JwtBearer
配置JWT信息
"JwtSettings": {
  "Issuer": "YourIssuer",
  "Audience": "YourAudience",
  "SecretKey": "YourSuperSecretKeyThatShouldBeLongAndSecure"
},
创建JWT配置映射实体类
public class JwtSettings
{
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public string SecretKey { get; set; }
}
JWT认证服务配置和添加认证中间件
  // 配置 JWT 设置
  var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>();

  // 添加认证服务
  builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
      .AddJwtBearer(options =>
      {
          options.TokenValidationParameters = new TokenValidationParameters
          {
              ValidateIssuer = true,
              ValidateAudience = true,
              ValidateLifetime = true,
              ValidateIssuerSigningKey = true,
              ValidIssuer = jwtSettings.Issuer,
              ValidAudience = jwtSettings.Audience,
              IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
          };
          options.Events = new JwtBearerEvents
          {
              OnForbidden = async context =>
              {
                  context.Response.StatusCode = 403;
                 // await context.Response.WriteAsync("服务端拒绝访问:当前身份没有权限访问,请联系管理员开放权限");
                  await context.Response.WriteAsJsonAsync(new { message = "服务端拒绝访问:当前身份没有权限访问,请联系管理员开放权限" });
              }
          };
      });

var app = builder.Build();
app.UseAuthentication();

实现自定义策略授权

定义权限策略类
 /// <summary>
 /// 权限策略类
 /// </summary>
 public class PermissionRequirement : IAuthorizationRequirement
 {
     public string _roleName { get; }

     public PermissionRequirement(string RoleName)
     {
         _roleName = RoleName;
     }
 }

IAuthorizationRequirement 用于定义授权策略中的具体要求。当你需要实现自定义的授权逻辑时,可以创建实现该接口的类,然后将这些类添加到授权策略中。在用户请求访问受保护资源时,授权系统会检查用户是否满足这些要求。

定义策略处理类
public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
{
    protected  override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == ClaimTypes.Role && c.Value == requirement._roleName))
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }

        await Task.CompletedTask;
    }
}

AuthorizationHandler 是一个抽象类,位于 Microsoft.AspNetCore.Authorization 命名空间下。它的主要作用是根据特定的授权要求(实现 IAuthorizationRequirement 接口的类)来判断用户是否有权限访问受保护的资源。开发者需要继承 AuthorizationHandler<TRequirement> 泛型类(其中 TRequirement 是具体的授权要求类型),并重写 HandleRequirementAsync 方法来实现自定义的授权逻辑。

配置自定义策略授权和添加授权中间件
  // 添加自定义授权策略
  builder.Services.AddAuthorization(options =>
  {
      options.AddPolicy("AdminPolicy", policy => policy.Requirements.Add(new PermissionRequirement("Admin")));
      options.AddPolicy("SuperAdminPolicy", policy => policy.Requirements.Add(new PermissionRequirement("SuperAdmin")));
  });

var app = builder.Build();
app.UseAuthorization(); //注意,授权中间件放在认证中间件后面
依赖注入
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();

实现接口

创建请求实体
 public class LoginRequest
 {
     public string username { get; set; }

     public string password { get; set; }
 }
创建接口

在管理员和超级管理员接口前加上特性进行区分。

 [Route("api/[controller]")]
 [ApiController]
 public class TestController : ControllerBase
 {
     private readonly IConfiguration _configuration;

     public TestController(IConfiguration configuration)
     {
         _configuration = configuration;
     }


     [HttpPost("login")]
     public IActionResult Login(LoginRequest request)
     {
         string role = string.Empty;
         if (request.username == "a" && request.password == "123456")
         {
             role = "Admin";
         }
         else if (request.username == "b" && request.password == "123456")
         {
             role = "SuperAdmin";
         }

         var jwtSettings = _configuration.GetSection("JwtSettings").Get<JwtSettings>();

         var tokenHandler = new JwtSecurityTokenHandler();
         var key = Encoding.ASCII.GetBytes(jwtSettings.SecretKey);
         var tokenDescriptor = new SecurityTokenDescriptor
         {
             Subject = new ClaimsIdentity(new[]
             {
                 new Claim(ClaimTypes.Name, request.username),
                 new Claim(ClaimTypes.Role, role)
             }),
             Issuer = jwtSettings.Issuer,
             Audience = jwtSettings.Audience,
             Expires = DateTime.UtcNow.AddHours(1),
             SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
         };
         var token = tokenHandler.CreateToken(tokenDescriptor);
         string tokenString = tokenHandler.WriteToken(token);

         return Ok(new { token = tokenString, message = "登录成功" });
     }


     [HttpGet("GetAdminUserInformation")]
     [Authorize(Policy = "AdminPolicy")]
     public IActionResult GetAdminUserInformation()
     {
         return Ok(new { Name = "管理员", Age = 18, Sex = "男" });
     }

     [HttpGet("GetSuperAdminInformation")]
     [Authorize(Policy = "SuperAdminPolicy")]
     public IActionResult GetSuperAdminInformation()
     {
         return Ok(new { Name = "超级管理员", Age = 19, Sex = "男" });
     }
 }

请求测试

用管理员账号访问管理员接口ok,访问超级管理员接口返回403

用超级管理员账号访问超级管理员接口ok,访问管理员接口同样返回403

posted @ 2025-02-03 15:44  相遇就是有缘  阅读(103)  评论(0)    收藏  举报