完整框架项目源码地址:https://download.csdn.net/download/yigu4011/87788956?spm=1001.2014.3001.5503
JWT是什么?校验逻辑?授权过程?这里就不过多的阐述了,直接上代码
在appsettings.json中配置jwt参数的值
SecretKey必须大于16个字符
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AppSettings": {
//数据库连接字符串
"ConnectionString": "server=192.168.132.131;uid=sa;pwd=6014359wQ@!;database=MagCoreDB",
"JwtSetting": {
"Issuer": "jwtIssuer", //颁发者
"Audience": "jwtAudience", //可以给哪些客户端使用
"SecretKey": "yehjsiwkjhuhgyehsnd" //加密Key
}
}
}
Nuget安装以下三个包:
在web.core.model中新建类TokenModel:
///
/// 令牌
///
public class TokenModel
{
///
/// Id
///
public string Uid { get; set; }
///
/// 角色
///
public string Role { get; set; }
}
新建JwtHelper.cs帮助类
public class JwtHelper
{
///
/// 颁发JWT字符串
///
///
///
public static string IssueJwt(TokenModel tokenModel)
{
//获取Appsetting配置
string iss = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });
string aud = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" });
string secret = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });
//var claims = new Claim[] //old
var claims = new List
{
/*
* 特别重要:
1、这里将用户的部分信息,比如 uid 存到了Claim 中,如果你想知道如何在其他地方将这个 uid从 Token 中取出来,请看下边的SerializeJwt() 方法,或者在整个解决方案,搜索这个方法,看哪里使用了!
2、你也可以研究下 HttpContext.User.Claims ,具体的你可以看看 Policys/PermissionHandler.cs 类中是如何使用的。
*/
new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),
new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,
//这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间
new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(1000).ToString()),
new Claim(JwtRegisteredClaimNames.Iss,iss),
new Claim(JwtRegisteredClaimNames.Aud,aud),
};
// 可以将一个用户的多个角色全部赋予;
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 jwt = new JwtSecurityToken(
issuer: iss,
claims: claims,
signingCredentials: creds);
var jwtHandler = new JwtSecurityTokenHandler();
var encodedJwt = jwtHandler.WriteToken(jwt);
return encodedJwt;
}
///
/// 解析
///
///
///
public static TokenModel SerializeJwt(string jwtStr)
{
var jwtHandler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
object role;
try
{
jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
var tm = new TokenModel
{
Uid = jwtToken.Id.ToString(),
Role = role != null ? role.ToString() : "",
};
return tm;
}
}
在HomeController中新建Login接口来获取Token
///
/// 获取Token
///
///
///
///
[HttpGet]
[AllowAnonymous]
public async Task Login(string rolename, string password)
{
string jwtStr = string.Empty;
bool suc = false;
if (rolename != null && password != null)
{
if (rolename != "Admin" && password != "123456")
{
jwtStr = "输入不正确!";
}
else
{
// 将用户id和角色名,作为单独的自定义变量封装进 token 字符串中。
TokenModel tokenModel = new TokenModel { Uid = "1", Role = rolename };
jwtStr = JwtHelper.IssueJwt(tokenModel);//登录,获取到一定规则的 Token 令牌
suc = true;
}
}
else
{
jwtStr = "输入不能为空!";
}
return Ok(new
{
success = suc,
token = jwtStr
});
}
运行项目,输入Admin,123456,获取Token:
JWT权限验证,就需要开启验证,然后输入token令牌,然后在SwaggerSetUp.cs的AddSwaggerSetup方法的AddSwaggerGen服务中,添加代码:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("V1", new OpenApiInfo
{
// {ApiName} 定义成全局变量,方便修改
Version = "V1",
Title = $"{ApiName} 接口文档——.NetCore 6.0",
Description = $"{ApiName} HTTP API V1",
//Contact = new OpenApiContact { Name = ApiName, Email = "[email protected]", Url = new Uri("https://www.jianshu.com/u/94102b59cc2a") },
//License = new OpenApiLicense { Name = ApiName, Url = new Uri("https://www.jianshu.com/u/94102b59cc2a") }
});
c.OrderActionsBy(o => o.RelativePath);
var xmlPath = Path.Combine(AppContext.BaseDirectory, "Mag.Core.API.xml");//这个就是刚刚配置的xml文件名
c.IncludeXmlComments(xmlPath, true);//默认的第二个参数是false,这个是controller的注释,记得修改
var xmlModelPath = Path.Combine(AppContext.BaseDirectory, "Mag.Core.Model.xml");//这个就是Model层的xml文件名
c.IncludeXmlComments(xmlModelPath);
// 在header中添加token,传递到后台
c.OperationFilter();
#region Token绑定到ConfigureServices
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.ApiKey
});
#endregion
});
运行项目,就可以看见JWT验证入口:
在SetUp文件夹里面新建注册方法AuthorizationSetup.cs
public static class AuthorizationSetup
{
public static void AddAuthorizationSetup(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
// 1【授权】、这个和上边的异曲同工,好处就是不用在controller中,写多个 roles 。
// 然后这么写 [Authorize(Policy = "Admin")]
services.AddAuthorization(options =>
{
options.AddPolicy("User", policy => policy.RequireRole("User").Build());
options.AddPolicy("System", policy => policy.RequireRole("System").Build());
options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System"));
});
//读取配置文件
var symmetricKeyAsBase64 = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
var signingKey = new SymmetricSecurityKey(keyByteArray);
var Issuer = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });
var Audience = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" });
// 令牌验证参数
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = Issuer,//发行人
ValidateAudience = true,
ValidAudience = Audience,//订阅人
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(30),
RequireExpirationTime = true,
};
//2.1【认证】、core自带官方JWT认证
// 开启Bearer认证
services.AddAuthentication("Bearer")
// 添加JwtBearer服务
.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;
}
};
});
}
}
在program.cs里面注册服务,开启服务
//jwt授权验证
builder.Services.AddAuthorizationSetup();
app.UseAuthentication();
在BaseApiController里面设置全局权限验证:
没有验证权限会提示401:
设置获取token的时候 [AllowAnonymous],无需验证:
获取一个Admin权限的token,并放到swagger 的权限验证里面:
设置测试方法:需要System的权限才能访问:
上面我们是用的Admin的权限,所以会提示403错误
新增解析Token的方法:
///
/// 解析Token
///
///
[HttpGet]
[Authorize]
public IActionResult ParseToken()
{
//需要截取Bearer
var tokenHeader = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
var user = JwtHelper.SerializeJwt(tokenHeader);
return Ok(user);
}
解析出来的role是Admin。