- A+
所属分类:.NET技术
目录
简介
- Jwt分为三段 通过远点分割
- header => 描述这个token加密方式
- PlayLoad => 有效载荷,用户信息+自定义Claims信息Verify
- Signature => 签名, (头部信息base64处理,有效载荷base64处理) + 密钥
- 示例 :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJFeHRlbmRlZDEiOiLml6Dkv6Hmga8iLCJFeHRlbmRlZDIiOiIiLCJFeHRlbmRlZDMiOiIiLCJFeHRlbmRlZDQiOiIiLCJFeHRlbmRlZDUiOiIiLCIxIjoi57O757uf566h55CG5ZGYIiwiMiI6IueUqOaIt-euoeeQhuWRmCIsImV4cCI6MTY4ODkwMTA2NiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDg4IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDg4In0.7J1J7yWj4ELHJZIwLKnT4RgcMu3rGAX5ACBFfCS0LWM
基于.Net Core 验证方式
- 生成jwtToken
- 标记[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
- 验证 Issuer
- 验证 Audience
- 验证 SecurityKey
- 验证自定义验证
- 验证完成可以正常访问接口
验证的那几步顺序可以直接在自定义验证中验证
Jwt获取Token
引入三方包
<ItemGroup> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.31.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" /> </ItemGroup>
生成Token
using Microsoft.IdentityModel.Tokens; using Programming.DotNetCore.Function.Entity.Jwt; using Programming.DotNetCore.Function.Interface.PasswordService; using Programming.DotNetCore.Function.Password.Entity; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace Programming.DotNetCore.Function.Password { public class JwtServices : IPassWordService { public string GetToken(UserInfo user,JwtConfig jwtConfig) { List<Claim> claims = new List<Claim> { new Claim(ClaimTypes.Name, user.UserName ?? ""), new Claim("Extended1", user.Extended1 ?? ""), new Claim("Extended2", user.Extended2 ?? ""), new Claim("Extended3", user.Extended3 ?? ""), new Claim("Extended4", user.Extended4 ?? ""), new Claim("Extended5", user.Extended5 ?? ""), }; if (user.Role is not null) { foreach (var item in user.Role) { claims.Add(new Claim(item.Id.ToString(), item.Role)); } } if(jwtConfig.SecurityKey == null) { throw new Exception("JwtConfig.SecurityKey 不能为空"); } SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey)); SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); JwtSecurityToken token = new JwtSecurityToken( issuer:jwtConfig.Issuer, audience:jwtConfig.Audience, claims:claims, expires: DateTime.UtcNow.AddMinutes(jwtConfig.ExpiresMinutes), signingCredentials:creds ); string resultToken = new JwtSecurityTokenHandler().WriteToken(token); return resultToken; } } }
UserInfo
namespace Programming.DotNetCore.Function.Password.Entity { public class UserInfo { public string? UserName { get; set; } public List<RoleInfo>? Role { get; set; } public string? Extended1 { get; set; } public string? Extended2 { get; set; } public string? Extended3 { get; set; } public string? Extended4 { get; set; } public string? Extended5 { get; set; } } }
JwtConfig
namespace Programming.DotNetCore.Function.Entity.Jwt { public class JwtConfig { public string? Audience { get; set; } public string? Issuer { get; set; } public string? SecurityKey { get; set; } public int ExpiresMinutes { get; set; } } }
WebApi测试(获取Token)
Program.cs
//读取Jwt配置 builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("JwtTokenOptions"));
appsetting.json
{ "JwtTokenOptions": { "Issuer": "http://localhost:5088", "Audience": "http://localhost:5088", "SecurityKey": "kq4DY5N1eFJhscOkI7Zp4Nd0WNy9d9AEsN6Yjgdv9OxLyol66tzGBKT_7vwolN7GZ8EDwqJBwccjDJfb81ws5s3sbbP5wUzQ3-PcTSsD-Rueiu2rsOUZwg_NR3RBCwmtouV-832YV2trCjNTawLB1z0LMukWGFNaAJVZ8WdQcrYn6a0ko5oVhZqaHBgsCLEGiqPtoFsiCcrJTz1IvXHk9_cDSr2hwEmSl18GlkOtgCHFH8aidYth3aQHRHuClTi6Y9mYRJtqqK-FNQYq4ZP23DSGZGFejJFTnM9YMpppuTMLklhSGySwX8rfjZ_0L5ac18nHaykTaiC2fvH00W42qQ" } }
Controller
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Programming.DotNetCore.Function.Entity.Jwt; using Programming.DotNetCore.Function.Interface.PasswordService; using Programming.DotNetCore.Function.Password.Entity; namespace Cnpc.Com.Ioc.WebApp.Controllers { [Route("api/[controller]/[Action]")] [ApiController] public class TestJwtController : ControllerBase { IPassWordService _passWordService; JwtConfig _jwtconfig; public TestJwtController(IPassWordService passWordService,IOptions<JwtConfig> jwtconfig) { _passWordService = passWordService; _jwtconfig = jwtconfig.Value; } [HttpGet] public IActionResult Login(string userName,string passWord) { string token = _passWordService.GetToken(new() { UserName = userName, Extended1 = "无信息", Role = new List<RoleInfo>() { new RoleInfo() { Id = "1",Role="系统管理员"} , new RoleInfo() { Id = "2",Role="用户管理员"} , } }, new() { Audience = _jwtconfig.Audience, Issuer= _jwtconfig.Issuer, SecurityKey= _jwtconfig.SecurityKey, ExpiresMinutes = 5, }); return new JsonResult(new { token = token }); } } }
.Net Core 验证(webApi)
Progarm
添加JWT验证
builder.Services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidIssuer = jwtConfig.Issuer, //发行人 ValidateAudience = true, ValidAudience = jwtConfig.Audience,//订阅人 ValidateIssuerSigningKey = true, //对称加密密钥 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey!)), ValidateLifetime = true, //验证失效时间 ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值 RequireExpirationTime = true, AudienceValidator = (audiences, securityToken, validationParameters) => { return true; }, LifetimeValidator = (notBefore,expires, securityToken, validationParameters) => { return true; } }; });
读取配置文件
JwtConfig jwtConfig = new JwtConfig(); builder.Configuration.Bind("JwtTokenOptions", jwtConfig);
添加Swagger支持,api右上角可以写Token
builder.Services.AddSwaggerGen(c => { //添加Jwt验证设置,添加请求头信息 c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, new List<string>() } }); //放置接口Auth授权按钮 c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "Value Bearer {token}", Name = "Authorization",//jwt默认的参数名称 In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中) Type = SecuritySchemeType.ApiKey }); }); ;
Contorller
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Programming.DotNetCore.Function.Entity.Jwt; using Programming.DotNetCore.Function.Interface.PasswordService; using Programming.DotNetCore.Function.Password.Entity; namespace Cnpc.Com.Ioc.WebApp.Controllers { [Route("api/[controller]/[Action]")] [ApiController] public class TestJwtController : ControllerBase { [HttpGet] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] public IActionResult TestApi() { //获取用户Claim信息 var user = HttpContext.User.Claims.Select(it => new { it.Type,it.Value}); return new JsonResult(user); } } }
.Net Core 授权
简介
可以在数据库中进一步验证访问接口的权限
Program.cs
//jwt 授权 builder.Services.AddAuthorization(options => { options.AddPolicy("JwtPolicy", policy => { //jwt 授权 policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) //这里为自定义授权指定一下类 .AddRequirements(new UserRoleRequirement(JwtBearerDefaults.AuthenticationScheme)); }); });
JwtAuthorization.cs
注意
验证中涉及到 IUserServices 和 JwtAuthorization, 需要在ioc容器中注册一下,我这里使用的是Autofac注册,如果使用系统自带的注册可以这么写:
- builder.Services.AddTransient<IUserServices, IUserServices>();
- builder.Services.AddTransient<IAuthorizationHandler, JwtAuthorization>();
Autofac 注册授权服务
using Autofac; using Autofac.Extras.DynamicProxy; using Castle.DynamicProxy; using Cnpc.Com.Ioc.Bll; using Cnpc.Com.Ioc.Dal; using Cnpc.Com.Ioc.IBll; using Cnpc.Com.Ioc.IDal; using Cnpc.Com.Ioc.Tools; using Cnpc.Com.Ioc.WebApp.Authorization; using Cnpc.Com.Ioc.WebApp.Filter.ActionFilter; using Microsoft.AspNetCore.Authorization; using Programming.DotNetCore.Function.Interface.PasswordService; using Programming.DotNetCore.Function.Password; namespace WepApiTest.Autofac { public class AutofacConfig : Module { protected override void Load(ContainerBuilder builder) { //ioc builder.RegisterType<JwtAuthorization>().As<IAuthorizationHandler>(); builder.RegisterType<UserServices>().As<IUserServices>(); } } }
using Cnpc.Com.Ioc.IBll; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; namespace Cnpc.Com.Ioc.WebApp.Authorization { public class UserRoleRequirement : IAuthorizationRequirement { public string AuthenticateScheme; public UserRoleRequirement(string authenticateScheme) { AuthenticateScheme = authenticateScheme; } } public class JwtAuthorization : AuthorizationHandler<UserRoleRequirement> { IUserServices userSercices; public JwtAuthorization(IUserServices userSercices) { this.userSercices = userSercices; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserRoleRequirement requirement) { string? userName = context.User.FindFirst(it => it.Type == ClaimTypes.Name)?.Value; if (userSercices.IsAdmin(userName!)) { context.Succeed(requirement); } else { context.Fail(); } return Task.CompletedTask; } } }
Controller
注意
唯一需要修改的地方就是这里, 指定Policy 为 Program.cs 中设置授权方案名称
- [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy = "JwtPolicy")]
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Programming.DotNetCore.Function.Entity.Jwt; using Programming.DotNetCore.Function.Interface.PasswordService; using Programming.DotNetCore.Function.Password.Entity; namespace Cnpc.Com.Ioc.WebApp.Controllers { [Route("api/[controller]/[Action]")] [ApiController] public class TestJwtController : ControllerBase { [HttpGet] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Policy = "JwtPolicy")] public IActionResult TestApi() { var user = HttpContext.User.Claims.Select(it => new { it.Type,it.Value}); return new JsonResult(user); } } }