1、建.NetCore的WebApi项目,安装包IdentityModel,Microsoft.AspNetCore.Authentication.JwtBearer,Microsoft.AspNetCore.Authorization
2、注册服务和中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Text; using System.Threading.Tasks; namespace WebApiJwt.Utils { public static class AuthorizationSetup { public static void AddAuthorizationSetup( this IServiceCollection services) { if (services == null ) throw new ArgumentNullException(nameof(services)); // 策略 services.AddAuthorization(options => { options.AddPolicy( "Admin" , policy => policy.RequireRole( "Admin" , "System" )); }); //读取配置文件 var symmetricKeyAsBase64 = ConfigHelper.GetSectionValue( "JwtSetting:SecretKey" ); var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var Issuer = ConfigHelper.GetSectionValue( "JwtSetting:Issuer" ); var Audience = ConfigHelper.GetSectionValue( "JwtSetting:Audience" ); // 令牌验证参数 var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true , //是否验证签名,不验证的画可以篡改数据,不安全 IssuerSigningKey = signingKey, //解密的密钥 ValidateIssuer = true , //是否验证发行人,就是验证载荷中的Iss是否对应ValidIssuer参数 ValidIssuer = Issuer, //发行人 ValidateAudience = true , //是否验证订阅人,就是验证载荷中的Aud是否对应ValidAudience参数 ValidAudience = Audience, //订阅人 ValidateLifetime = true , //是否验证过期时间,过期了就拒绝访问 ClockSkew = TimeSpan.Zero, //缓冲过期时间,真正过期时间=配置过期时间+缓冲过期时间,默认5分钟 RequireExpirationTime = true , //token需要设置过期时间 }; // 开启Bearer认证,添加JwtBearer服务 services.AddAuthentication(o => { o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { o.TokenValidationParameters = tokenValidationParameters; o.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { // 如果过期,则把<是否过期>添加到,返回头信息中 if (context.Exception.GetType() == typeof (SecurityTokenExpiredException)) { context.Response.Headers.Add( "Token-Expired" , "true" ); } return Task.CompletedTask; } }; }); } } } |
Startup.cs
注册服务:services.AddAuthorizationSetup();
添加中间件:app.UseAuthentication(); app.UseAuthorization();
3、生成Token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using WebApiJwt.Models; namespace WebApiJwt.Utils { public class JwtHelper { /// <summary> /// 颁发JWT字符串 /// </summary> /// <param name="tokenModel"></param> /// <returns></returns> public static object IssueJwt(TokenModel tokenModel) { DateTime authTime = DateTime.UtcNow; string iss = ConfigHelper.GetSectionValue( "JwtSetting:Issuer" ); //颁发者 string aud = ConfigHelper.GetSectionValue( "JwtSetting:Audience" ); //可以给哪些客户端使用 string secret = ConfigHelper.GetSectionValue( "JwtSetting:SecretKey" ); //加密的Key //token有效期 分钟 DateTime expiresAt = authTime.AddMinutes(Convert.ToDouble(ConfigHelper.GetSectionValue( "JwtSetting:ExpireMinutes" ))); //设置Claim var claims = new List<Claim> { //claim默认配置 new Claim(JwtRegisteredClaimNames.Jti, tokenModel.UserId.ToString()), new Claim(JwtRegisteredClaimNames.Iat, $ "{new DateTimeOffset(authTime).ToUnixTimeSeconds()}" ), new Claim(JwtRegisteredClaimNames.Nbf,$ "{new DateTimeOffset(authTime).ToUnixTimeSeconds()}" ) , //过期时间,JWT有缓冲过期时间,所以过期时间到了会延迟一会儿再过期,影响不大。 new Claim(JwtRegisteredClaimNames.Exp,$ "{new DateTimeOffset(expiresAt).ToUnixTimeSeconds()}" ), new Claim(ClaimTypes.Expiration, expiresAt.ToString()), new Claim(JwtRegisteredClaimNames.Iss,iss), //颁发者 new Claim(JwtRegisteredClaimNames.Aud,aud), //可以给哪些客户端使用 new Claim(ClaimTypes.Name, tokenModel.UserName), new Claim(ClaimTypes.Email, tokenModel.Email) }; //设置Claim角色 claims.AddRange(tokenModel.Role.Split( ',' ).Select(s => new Claim(ClaimTypes.Role, s))); //秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常) var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwtSecurityToken = new JwtSecurityToken(issuer: iss, claims: claims, signingCredentials: creds); var jwtHandler = new JwtSecurityTokenHandler(); var encodedJwt = jwtHandler.WriteToken(jwtSecurityToken); //存储 Token 信息 var jwt = new { UserId = tokenModel.UserId, Token = encodedJwt, Auths = new DateTimeOffset(authTime).ToUnixTimeSeconds(), Expires = new DateTimeOffset(expiresAt).ToUnixTimeSeconds(), Success = true }; return jwt; } /// <summary> /// 解析 /// </summary> /// <param name="jwtStr"></param> /// <returns></returns> public static TokenModel SerializeJwt( string jwtStr) { var jwtHandler = new JwtSecurityTokenHandler(); if (!jwtHandler.CanReadToken(jwtStr)) { return null ; } string iss = ConfigHelper.GetSectionValue( "JwtSetting:Issuer" ); JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr); if (jwtToken.Issuer != iss) { return null ; //不正确 } if (jwtToken.ValidTo < DateTime.UtcNow) { return null ; //过期 } object role, userName, email; try { jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role); jwtToken.Payload.TryGetValue(ClaimTypes.Name, out userName); jwtToken.Payload.TryGetValue(ClaimTypes.Email, out email); } catch (Exception e) { Console.WriteLine(e); throw ; } var tm = new TokenModel { UserId = jwtToken.Id.ToString(), Role = role != null ? role.ToString() : "" , UserName = userName != null ? userName.ToString() : "" , Email = email != null ? email.ToString() : "" }; return tm; } } } |
配置
1 2 3 4 5 6 | "JwtSetting" : { "Issuer" : "jwtIssuer" , //颁发者 "Audience" : "jwtAudience" , //可以给哪些客户端使用 "SecretKey" : "abcdabcdabcdabcdabcdabcdabcdabcd" , //加密的Key,长度最少要32位,不然报错。 "ExpireMinutes" : "3" //token有效期3分钟 } |
辅助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using Microsoft.Extensions.Configuration; using System; namespace WebApiJwt.Utils { public class ConfigHelper { public readonly static IConfiguration configuration; static ConfigHelper() { configuration = new ConfigurationBuilder() .SetBasePath(Environment.CurrentDirectory) .AddJsonFile( "appsettings.json" , true , true ) .AddInMemoryCollection() .Build(); } public static string GetSectionValue( string key) { return configuration[key]; } public static string connectionString { get { return configuration[ "ConnectionStrings:Default" ]; } } } } |
4、ApiController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using WebApiJwt.Models; using WebApiJwt.Utils; namespace WebApiJwt.Controllers { [Route( "api/[controller]/[action]" )] [ApiController] public class AuthorizeController : ControllerBase { /// <summary> /// 生成令牌 /// </summary> /// <returns></returns> [HttpGet] public string GenToken() { TokenModel tokenModel = new TokenModel { UserId = "1001" , Role = "Admin" , Email = "908085411@qq.com" , UserName = "邢帅杰" }; string jwtToken = JsonConvert.SerializeObject(JwtHelper.IssueJwt(tokenModel)); return jwtToken; } /// <summary> /// 需要Admin权限 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Roles = "Admin" )] //[Authorize(Policy = "Admin")]//使用策略来映射多个角色 public IActionResult Admin() { return Ok( "admin........" ); } /// <summary> /// 解析Token /// </summary> /// <returns></returns> [HttpGet] [Authorize] public IActionResult AnalysisToken() { var tokenHeader = HttpContext.Request.Headers[ "Authorization" ].ToString().Replace( "Bearer " , "" ); var user = JwtHelper.SerializeJwt(tokenHeader); return Ok(user); } } } |
5、 生成:https://localhost:44393/api/Authorize/GenToken,解析:https://localhost:44393/api/Authorize/AnalysisToken,在header中添加 Authorization:Bearer token
参考:
https://www.cnblogs.com/danvic712/p/10331976.html
https://blog.csdn.net/weixin_42045719/article/details/91973878
http://cn.voidcc.com/question/p-vckjdtzx-hv.html
https://www.cnblogs.com/danvic712/p/10331976.html
https://blog.csdn.net/baidu_35726140/article/details/84867520
https://www.cnblogs.com/xwc1996/p/14058115.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】