# 首先安装 jwt 所需的 Nuget 包
Microsoft.AspNetCore.Authentication.JwtBearer | |
<!-- Swagger 授权扩展包 --> | |
Swashbuckle.AspNetCore.Filters |
# 创建一个类里面写两个方法用来配置 Swagger 和 JWT
/// <summary> | |
/// Jwt 以及 Swagger 扩展配置 | |
/// </summary> | |
public static class JwtExtension | |
{ | |
/// <summary> | |
/// Jwt 配置授权 | |
/// </summary> | |
/// <param name="services"></param> | |
/// <param name="builder"></param> | |
/// <returns></returns> | |
public static AuthenticationBuilder MyAddAuthentication(this IServiceCollection services, WebApplicationBuilder builder) | |
{ | |
return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) | |
.AddJwtBearer(options => | |
{ | |
options.TokenValidationParameters = new TokenValidationParameters | |
{ | |
ValidateIssuer = true,// 是否验证 Issuer | |
ValidateAudience = true,// 是否验证 Audience | |
ValidateLifetime = true,// 是否验证失效时间 | |
ClockSkew = TimeSpan.FromSeconds(30),// 有效时间 | |
ValidateIssuerSigningKey = true,// 是否验证 SecurityKey | |
ValidAudience = builder.Configuration.GetSection("JWT:Audience").Value,// Audience | |
ValidIssuer = builder.Configuration.GetSection("JWT:Issuer").Value,// Issuer,这两项和前面签发 jwt 的设置一致 | |
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("JWT:SecurityKey").Value))// SecurityKey | |
}; | |
options.Events = new JwtBearerEvents | |
{ | |
// 此处为权限验证失败后触发的事件 | |
OnChallenge = context => | |
{ | |
// 此处代码为终止.Net Core 默认的返回类型和数据结果,这个很重要哦,必须 | |
context.HandleResponse(); | |
// 自定义自己想要返回的数据结果,我这里要返回的是 Json 对象,通过引用 Newtonsoft.Json 库进行转换 | |
var payload = JsonConvert.SerializeObject(new Result(DataModel.Enum.HttpCode.Unauthorized, DataModel.Enum.HttpMessage.Unauthorized)); | |
// 自定义返回的数据类型 | |
context.Response.ContentType = "application/json"; | |
// 自定义返回状态码,默认为 401 我这里改成 200 | |
context.Response.StatusCode = 200; | |
// 输出 Json 数据结果 | |
context.Response.WriteAsync(payload); | |
return Task.FromResult(0); | |
}, | |
// 权限不够,例如角色组 Role 无该权限 | |
OnForbidden = context => | |
{ | |
// 自定义自己想要返回的数据结果,我这里要返回的是 Json 对象,通过引用 Newtonsoft.Json 库进行转换 | |
var payload = JsonConvert.SerializeObject(new Result(DataModel.Enum.HttpCode.Forbidden, DataModel.Enum.HttpMessage.Forbidden)); | |
// 自定义返回的数据类型 | |
context.Response.ContentType = "application/json"; | |
// 自定义返回状态码,默认为 403 我这里改成 200 | |
context.Response.StatusCode = 200; | |
// 输出 Json 数据结果 | |
context.Response.WriteAsync(payload); | |
return Task.FromResult(0); | |
} | |
}; | |
}); | |
} | |
/// <summary> | |
/// Swagger 配置信息 | |
/// </summary> | |
/// <param name="services"></param> | |
/// <returns></returns> | |
public static IServiceCollection MyAddSwaggerGen(this IServiceCollection services) | |
{ | |
return services.AddSwaggerGen(c => | |
{ | |
// Swagger 文档版本控制(ApiExplorerSettings 中 GroupName 需要和 ApiVersion 枚举定义一致) | |
typeof(DataModel.Enum.ApiVersion).GetEnumNames().ToList().ForEach(name => | |
{ | |
c.SwaggerDoc(name, new OpenApiInfo { Title = "DotNET6原生API", Version = name, Description = "<b><a href='https://gitee.com/lovetianci/TianciDotNET6API'>项目地址</a><b>" }); | |
}); | |
// 控制器注释(需在对应项目 > 属性 > 输出 > 文档文件:生成包含 API 文档的文件√) | |
c.IncludeXmlComments(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TianciDotNET6API.xml"), true); | |
// 实体类注释(需在对应项目 > 属性 > 输出 > 文档文件:生成包含 API 文档的文件√) | |
c.IncludeXmlComments(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DataModel.xml"), true); | |
// Summary 注释显示授权组名称信息 | |
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>(); | |
// 加这个带锁,false 为请求接口未授权将不会显示返回值例如 Forbidden | |
c.OperationFilter<SecurityRequirementsOperationFilter>(false); | |
var security = new OpenApiSecurityScheme | |
{ | |
In = ParameterLocation.Header, | |
Type = SecuritySchemeType.ApiKey, | |
// 如果是 ApiKey 则需要输入 Bearer token(中间有空格) | |
// 因为 Scheme 填写了 Bearer,所以 Swagger 只需输入 token 即可(2022 年 8 月 29 日) | |
// 如果是 http 则直接输入 token | |
Description = "直接在下框中输入token<br/><b>授权地址:/api/Login/Login</b>", | |
Name = "Authorization", | |
Scheme = "Bearer", | |
BearerFormat = "JWT" | |
}; | |
//oauth2 唯一不可变 | |
c.AddSecurityDefinition("oauth2", security); | |
// 添加全局安全要求,所需方案的字典(逻辑与)密钥必须对应于通过 AddSecurityDefinition 定义的方案 | |
// 如果方案是 oauth2 类型,则值是范围列表,否则必须是空数组 | |
//c.AddSecurityRequirement(new OpenApiSecurityRequirement { { security, new string[] { } } }); | |
// 请求头过滤,上面写了鉴权失败返回值所以这不需要 | |
//c.OperationFilter<AddResponseHeadersFilter>(); | |
}); | |
} | |
} |
# 配置文件 appsetting.json 中配置对应的密钥字段(模拟数据)
"JWT": { | |
"Issuer": "Client", | |
"Audience": "Server", | |
"SecurityKey": "EF1DA5B7-C4FA-4240-B997-7D1701BF9BE2" | |
}, |
# 将服务添加到容器
var builder = WebApplication.CreateBuilder(args); | |
// Swagger 配置 | |
builder.Services.MyAddSwaggerGen(); | |
// 注册 jwt | |
builder.Services.MyAddAuthentication(builder); |
# 配置 HTTP 请求管道
var app = builder.Build(); | |
// 认证 | |
app.UseAuthentication(); | |
// 授权 | |
app.UseAuthorization(); | |
// 开启 Swagger | |
app.UseSwagger(); | |
app.UseSwaggerUI(c => | |
{ | |
// Swagger 文档版本控制 | |
typeof(DataModel.Enum.ApiVersion).GetEnumNames().ToList().ForEach(name => | |
{ | |
c.SwaggerEndpoint($"/Swagger/{name}/Swagger.json", $"{name}"); | |
// 设置首页 index 为 Swagger | |
c.RoutePrefix = string.Empty; | |
// 设置网页 Title | |
c.DocumentTitle = "TianciDotNET6API接口文档"; | |
// 设置为 none 默认可折叠所有方法 | |
c.DocExpansion(DocExpansion.None); | |
// 设置为 - 1 可不显示 models | |
c.DefaultModelsExpandDepth(-1); | |
}); | |
}); |
# 登录获取 Token 的方法可以简单定义一个
# _configuration 为注入的 IConfiguration 接口
string userToken = string.Empty; | |
var claims = new[] | |
{ | |
new Claim(ClaimTypes.Name, "UserName"), | |
new Claim(ClaimTypes.Role, "RoleName"), | |
new Claim(JwtRegisteredClaimNames.Sub,"UserName"), | |
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()) | |
}; | |
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("JWT:SecurityKey").Value)); | |
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); | |
var token = new JwtSecurityToken | |
( | |
issuer: _configuration.GetSection("JWT:Issuer").Value,// 签发人 | |
audience: _configuration.GetSection("JWT:Audience").Value,// 受众人 | |
claims: claims, | |
expires: DateTime.Now.AddMinutes(30),// 过期时间 | |
signingCredentials: creds | |
); | |
userToken = new JwtSecurityTokenHandler().WriteToken(token); |