JWT不管是基于角色,还是自定义策略,实现的步骤都是大同小异的,基于自定义策略的步骤如下:
1、appsettings.json中配置JWT参
2、添加身份认证和授权服务和中间件,并设置为策略模式和策略名称
3、定义生成Token的方法和验证Toekn参数的方法
4、登录时验证身份并分发Toekn
5、继承AuthorizationHandler<IAuthorizationRequirement>,实现鉴权的规则
接下来看看具体实现。
JWT配置
"JWTConfig": {"Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890","Issuer": "gsw","Audience": "everone","Expires": 10000}
实现自定义策略
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;var builder = WebApplication.CreateBuilder();
//绑定JWT配置文年
var jwtConfig = new JWTConfig();
builder.Configuration.GetSection("JWTConfig").Bind(jwtConfig);
builder.Services.AddSingleton(jwtConfig);
//这里是注入权限数据,也可以放在缓存中,以便鉴权时用
builder.Services.AddSingleton(new List<Permission> { new Permission { RoleName = "admin", Url = "/helloadmin", Method = "get" } });
//注入自定义策略处理类型
builder.Services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
//注入身分验证和授权,并且是Policy的名称为Permission
builder.Services.AddAuthorization(options =>{var permissionRequirement = new PermissionRequirement();options.AddPolicy("Permission", policy => policy.AddRequirements(permissionRequirement));}).AddAuthentication(options =>{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>{opt.RequireHttpsMetadata = false;opt.TokenValidationParameters = JwtToken.CreateTokenValidationParameters(jwtConfig);});var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
//map三个get请求,都是.RequireAuthorization("Permission"),基于Permission策略来验证的
app.MapGet("/hellosystem", (ILogger<Program> logger, HttpContext context) =>
{var message = $"hello,system,{context.User?.Identity?.Name}";logger.LogInformation(message);return message;
}).RequireAuthorization("Permission");app.MapGet("/helloadmin", (ILogger<Program> logger, HttpContext context) =>
{var message = $"hello,admin,{context.User?.Identity?.Name}";logger.LogInformation(message);return message;
}).RequireAuthorization("Permission");app.MapGet("/helloall", (ILogger<Program> logger, HttpContext context) =>
{var message = $"hello,all roles,{context.User?.Identity?.Name}";logger.LogInformation(message);return message;
}).RequireAuthorization("Permission");//登录,并分发Token
app.MapPost("/login", [AllowAnonymous] (ILogger<Program> logger, LoginModel login, JWTConfig jwtConfig) =>
{logger.LogInformation("login");if (login.UserName == "gsw" && login.Password == "111111"){var now = DateTime.UtcNow;var claims = new Claim[] {new Claim(ClaimTypes.Role, "admin"),new Claim(ClaimTypes.Name, "桂素伟"),new Claim(ClaimTypes.Sid, login.UserName),new Claim(ClaimTypes.Expiration, now.AddSeconds(jwtConfig.Expires).ToString())};var token = JwtToken.BuildJwtToken(claims, jwtConfig);return token;}else{return "username or password is error";}
});app.Run();
//登录实体
public class LoginModel
{public string? UserName { get; set; }public string? Password { get; set; }
}
//JWT配置文年
public class JWTConfig
{public string? Secret { get; set; }public string? Issuer { get; set; }public string? Audience { get; set; }public int Expires { get; set; }
}
//Token功能类
public class JwtToken
{public static dynamic BuildJwtToken(Claim[] claims, JWTConfig jwtConfig){var now = DateTime.UtcNow;var jwt = new JwtSecurityToken(issuer: jwtConfig.Issuer,audience: jwtConfig.Audience,claims: claims,notBefore: now,expires: now.AddSeconds(jwtConfig.Expires),signingCredentials: GetSigningCredentials(jwtConfig));var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);var response = new{Status = true,AccessToken = encodedJwt,ExpiresIn = now.AddSeconds(jwtConfig.Expires),TokenType = "Bearer"};return response;}static SigningCredentials GetSigningCredentials(JWTConfig jwtConfig){var keyByteArray = Encoding.ASCII.GetBytes(jwtConfig?.Secret!);var signingKey = new SymmetricSecurityKey(keyByteArray);return new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);}public static TokenValidationParameters CreateTokenValidationParameters(JWTConfig jwtConfig){var keyByteArray = Encoding.ASCII.GetBytes(jwtConfig?.Secret!);var signingKey = new SymmetricSecurityKey(keyByteArray);return new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = signingKey,ValidateIssuer = true,ValidIssuer = jwtConfig?.Issuer,ValidateAudience = true,ValidAudience = jwtConfig?.Audience,ClockSkew = TimeSpan.Zero,RequireExpirationTime = true,};}
}
//权限实本类
public class Permission
{public string? RoleName { get; set; }public string? Url { get; set; }public string? Method { get; set; }
}
//自定义策略授权时的参数类型,这时没参数,所以是个空类型
public class PermissionRequirement : IAuthorizationRequirement
{
}
//自定义策略授权的处理类型
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{private readonly List<Permission> _userPermissions;public PermissionHandler(List<Permission> permissions){_userPermissions = permissions;}protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement){if (context.Resource is DefaultHttpContext){var httpContext = context.Resource as DefaultHttpContext;var questPath = httpContext?.Request?.Path;var method = httpContext?.Request?.Method;var isAuthenticated = context?.User?.Identity?.IsAuthenticated;if (isAuthenticated.HasValue && isAuthenticated.Value){var role = context?.User?.Claims?.SingleOrDefault(s => s.Type == ClaimTypes.Role)?.Value;if (_userPermissions.Where(w => w.RoleName == role && w.Method?.ToUpper() == method?.ToUpper() && w.Url?.ToLower() == questPath).Count() > 0){context?.Succeed(requirement);}else{context?.Fail();}}}return Task.CompletedTask;}
}
运行结果如下:
1、没有登录,返回401
2、登录,取token
3、正确访问
4、没有授权访问,返回403