1 准备工作
1.1 WebApi.Controllers.JwtSettingModel
namespace WebApi.Test
{
/// <summary>
/// 【Jwt设置模型--纪录】
/// <remarks>
/// 摘要:
/// 通过该纪录中的属性成员实例存储“AppSettings.json”文件中的Jwt相关设置数据,为生成所有令牌(Token)字符串实例提供通用的数据支撑。
/// </remarks>
/// </summary>
public record JwtSettingModel
{
/// <summary>
/// 【Token签发机关】
/// <remarks>
/// 摘要:
/// 获取/设置用于生成所有令牌(Token)字符串实例的通用“签发机关”。
/// </remarks>
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 【Token订阅者】
/// <remarks>
/// 摘要:
/// 获取/设置用于生成所有令牌(Token)字符串实例的通用“订阅者”。
/// </remarks>
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 【加密字符串】
/// <remarks>
/// 摘要:
/// 获取/设置用于生成所有令牌(Token)字符串实例的通用“加密字符串”。
/// </remarks>
/// </summary>
public string SecretKey { get; set; }
}
}
1.2 WebApi.Controllers.LoginModel
namespace WebApi.Test
{
/// <summary>
/// 【登录模型--纪录】
/// <remarks>
/// 摘要:
/// 通过该纪录中的属性成员实例存储用于登录操作账户名及其密码。
/// </remarks>
/// </summary>
public record LoginModel
{
/// <summary>
/// 【账户名】
/// <remarks>
/// 摘要:
/// 获取/设置1个用于登录操作的账户名。
/// </remarks>
/// </summary>
public string Name { get; set; }
/// <summary>
/// 【密码】
/// <remarks>
/// 摘要:
/// 获取/设置1个用于登录操作的密码。
/// </remarks>
/// </summary>
public string Password { get; set; }
}
}
1.3 在“appsettings.json”文件中设置“Jwt”初始化所需要数据
//"Jwt相关设置数据,为生成所有令牌(Token)字符串实例提供通用的数据支撑。"
"JwtSetting": {
"Issuer": "Token签发机关",
"Audience": "Token订阅者",
"SecretKey": "8kh2luzmp0oq9wfbdeasygj647vr531n" //由发行者(开发者)提供的常量“秘钥”字符串,注意:该字符串不能小于16个字符。
},
2 定义“JwtBearer”依赖注入中间件
2.1 引用“JwtBearer” 中间件
通过Nuget引用:“Microsoft.AspNetCore.Authentication.JwtBearer”
2.2 在Program.cs定义“JwtBearer”依赖注入中间件
builder.Services.Configure<JwtSettingModel>(builder.Configuration.GetSection("JwtSetting"));
JwtSettingModel _jwtSettingModel = new JwtSettingModel();
builder.Configuration.Bind("JwtSetting", _jwtSettingModel);
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettingModel.SecretKey)),
// 指示在使用jwt中间件生成所有令牌(Token)中是否包含有“签发机关”的数据信息,当前设定为:true,即包含。
ValidateIssuer = true,
//所有令牌(Token)“签发机关”所对应的数据信息为:“Token签发机关”。
ValidIssuer = _jwtSettingModel.Issuer,
// 指示在使用jwt中间件生成指定令牌(Token)中是否包含有“订阅者”的数据信息,当前设定为:true,即包含。
ValidateAudience = true,
//所有令牌(Token)“订阅者”所对应的数据信息为:“Token订阅”
ValidAudience = _jwtSettingModel.Audience,
//指示是否所有令牌(Token)的过期时间进行限定,当前设定为:true,即限定。
RequireExpirationTime = true,
//指示是否对指定令牌(Token)的生命周期进行自动管理,当前设定为:true,即管理,
//当前所有令牌(Token)的生命周期结束时,程序必须重新生成1个新的指定令牌(Token)才能方法授权页面。
//使用当前时间与Token的Claims中的NotBefore和Expires对比后,进行管理。
ValidateLifetime = true,
//缓冲过期时间,所有令牌(Token)的总有效时间等于该时间加上jwt的过期时间,缓冲过期时间的默认值为“5分钟”,
//当前把缓冲过期时间设定为:0,指定令牌(Token)的总有效时间即为jwt的过期时间。
ClockSkew = TimeSpan.FromSeconds(0),
};
});
builder.Services.AddControllers();
3 定义需要Token认证的控件器方法
3.1 WebApi.Controllers.CustomerController
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using WebApi.Models;
using WebApi.Test;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class CustomerController : ControllerBase
{
#region 拷贝构造方法与变量
private JwtSettingModel _jwtSettingModel;
public CustomerController(IOptions<JwtSettingModel> options)
{
_jwtSettingModel = options.Value;
}
#endregion
/// <param name="login">登录模型记录的1个指定实例。</param>
/// <summary>
/// 【登录--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过登录操作获取1个指定用户(这里特指:“admin@yourStore.com”用户)的1个指定令牌(Token)字符串实例,为访问指定权限的Api提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定用户(这里特指:“admin@yourStore.com”用户)的1个指定令牌(Token)字符串实例。
/// </returns>
[HttpPost]
public async Task<MessageModel<string>> Login([FromBody] LoginModel login)
{
string _token = string.Empty;
if (login.Name == "admin@yourStore.com" && login.Password == "111111")
{
Claim[] _claimArray = new Claim[] {
new Claim(ClaimTypes.Name, login.Name),
new Claim(ClaimTypes.Role, "Administrator"),
new Claim(ClaimTypes.Role, "Register"),
new Claim(ClaimTypes.Role, "Guest"),
};
SymmetricSecurityKey _symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettingModel.SecretKey));
SigningCredentials _signingCredentials = new SigningCredentials(_symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
JwtSecurityToken _jwtSecurityToken = new JwtSecurityToken(
issuer: _jwtSettingModel.Issuer,
audience: _jwtSettingModel.Audience,
claims: _claimArray,
notBefore: DateTime.Now,//1个指定令牌(Token)字符串实例的生效时间,“生效时间”是当前时间,即立即生效。
expires: DateTime.Now.AddSeconds(180),//1个指定令牌(Token)字符串实例的生命周期,这里为3分钟。
signingCredentials: _signingCredentials);
_token = new JwtSecurityTokenHandler().WriteToken(_jwtSecurityToken);
}
//实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。
MessageModel<string> _tokenMessageModel = new MessageModel<string>()
{
Success = true,
Message = "获取成功!",
Response = _token,
};
return _tokenMessageModel;
}
}
}
3.2 WebApi.Controllers.ActionAuthorizationTestController
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WebApi.Models;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class ActionAuthorizationTestController : ControllerBase
{
[Authorize(Roles = "Administrator,Register,Guest")]
[HttpGet]
public async Task<MessageModel<string>> ActionTest()
{
//实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。
MessageModel<string> _actionAuthorizationMessageModel = new MessageModel<string>()
{
Success = true,
Message = "获取成功",
Response = "行为方法:“ActionTest”认证成功!",
};
return _actionAuthorizationMessageModel;
}
[Authorize]
[HttpGet]
public async Task<MessageModel<string>> ActionTest_1()
{
//实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。
MessageModel<string> _actionAuthorizationMessageModel = new MessageModel<string>()
{
Success = true,
Message = "获取成功",
Response = "行为方法:“ActionTest_1”认证成功!",
};
return _actionAuthorizationMessageModel;
}
}
}
3.3 WebApi.Controllers.ControllerAuthorizationTestController
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WebApi.Models;
namespace WebApi.Controllers
{
[Authorize(Roles = "Administrator,Register,Guest")]
[Route("[controller]/[action]")]
[ApiController]
public class ControllerAuthorizationTestController : ControllerBase
{
[HttpGet]
public async Task<MessageModel<string>> ControllerTest_1()
{
//实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。
MessageModel<string> _controllerAuthorizationMessageModel = new MessageModel<string>()
{
Success = true,
Message = "获取成功",
Response = "控制器方法:“ControllerTest_1”认证成功!",
};
return _controllerAuthorizationMessageModel;
}
[HttpGet]
public async Task<MessageModel<string>> ControllerTest_2()
{
//实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。
MessageModel<string> _controllerAuthorizationMessageModel = new MessageModel<string>()
{
Success = true,
Message = "获取成功",
Response = "控制器方法:“ControllerTest_2”认证成功!",
};
return _controllerAuthorizationMessageModel;
}
[HttpGet]
[AllowAnonymous]
public async Task<MessageModel<string>> ControlleTest_3()
{
//实例化消息模型录,对当前“Api”控制器行方法的执行操作结果进行存储,为客户端页面的渲染提供数据支撑。
MessageModel<string> _controllerAuthorizationMessageModel = new MessageModel<string>()
{
Success = true,
Message = "获取成功",
Response = "获取控制器不需要认证方法:“ControlleTest_3”!",
};
return _controllerAuthorizationMessageModel;
}
}
}
3.4 重构WebApi.Controllers.RoleController
/// <summary>
/// 【角色Api控制器--类】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过该类中的方法成员,为前端角色页面提供Api方法和数据支撑。
/// </remarks>
[Authorize(Roles = "Administrator")]
[Route("[controller]/[action]")]
[ApiController]
public class RoleController : ControllerBase
4 Postman调试需要Token认证Api方法
5 Swagger调试需要Token认证Api方法
5.1 引用“Swashbuckle.AspNetCore.Filters” 中间件
通过Nuget引用:“Swashbuckle.AspNetCore.Filters”
5.2 开启“Swagger/index.html”页面对Token认证Api方法的访问调试
//通过AddSwaggerGen依赖注入中间,获取Api控制器方法的版本控制信息和注释等数据信息,依赖注入.Net7框架的内置容器中,为在“index.html”页面上渲染显示这些信息,作好预处理操作。
builder.Services.AddSwaggerGen(options => {
options.SwaggerDoc("v1",
new OpenApiInfo
{
Version = "v1",
Title = $"接口文档—{RuntimeInformation.FrameworkDescription}",
});
//获取"UserServer.xml"文件的文件名。
string _xmlFileName = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
//获取"WebApi.xml"文件的绝对路径。
string _xmlFilePath = Path.Combine(AppContext.BaseDirectory, _xmlFileName);
//把控件器行为方法中的注释信息加载到"Swagger/index.html"页面中的控件器行为方法进行渲染显示。
//注意:如果不在“*.csproj”文件中启用“<GenerateDocumentationFile>true</GenerateDocumentationFile>”配置,下面语句会出现逻辑异常。
options.IncludeXmlComments(_xmlFilePath, true);
// 开启“Swagger/index.html”页面对Token认证Api方法的访问调试。
// 开启加权小锁
options.OperationFilter<AddResponseHeadersFilter>();
options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
// 在header中添加token,传递到后台
options.OperationFilter<SecurityRequirementsOperationFilter>();
// 必须是 oauth2
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.ApiKey
});
});
6 Vue调试需要Token认证Api方法
6.1 src\store\index.js
import {createStore} from 'vuex';
export default createStore({
state: {
//通过全局变量获取实例化“TokenJwt”字符串,或对“TokenJwt”字符串进行全局化存储。
token: localStorage.getItem('Token') ? localStorage.getItem('Token') : '',
},
getters: {},
mutations: {
//通过该方法把“TokenJwt”字符串,进行全局存储。
saveToken: function(state, data) {
state.token = data;
localStorage.setItem("Token", data);
},
},
actions: {},
modules: {}
})
6.2 src\views\LoginView.vue
<template>
<el-form :model="formLogin" label-position="left" label-width="0px" class="demo-ruleForm login-container">
<h3 class="title">系统登录</h3>
<el-form-item prop="account">
<el-input type="text" v-model="formLogin.account" auto-complete="off" placeholder="账号"></el-input>
</el-form-item>
<el-form-item prop="checkPass">
<el-input v-model="formLogin.checkPass" auto-complete="off" show-password placeholder="密码"></el-input>
</el-form-item>
<el-form-item style="width:100%;">
<el-button type="primary" style="width:100%;" @click="submitLogin">
登录
</el-button>
</el-form-item>
</el-form>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
formLogin: {
account: 'admin@yourStore.com',
checkPass: '111111',
},
};
},
methods: {
async submitLogin() {
let loginParams = {
name: this.formLogin.account,
password: this.formLogin.checkPass
};
let res = await axios.post('https://localhost:7043/Customer/Login', JSON.stringify(loginParams));
let userToken = 'Bearer ' + res.data.response;
this.$store.commit("saveToken", userToken);
//console.log(this.$store.state.token);
if (res.status == 200) {
let token = localStorage.getItem('Token');
console.log(token);
if (token === null || token === '') {
await this.$router.replace(this.$route.query.redirect ? this.$route.query.redirect : "/");
}
await this.$router.replace(this.$route.query.redirect ? this.$route.query.redirect : "Users/Role");
} else {
this.$message.error(res.msg);
}
},
},
mounted() {
//把Token字符串添加到Header拦截守卫。
axios.interceptors.request.use(
config => {
if (localStorage.getItem('Token')) {
config.headers.Authorization = localStorage.getItem('Token');
}
return config;
},
error => {
return Promise.reject(error);
});
},
}
</script>
6.3 src\views\LoginView.vue
<template>
<h1>Test-1View-----Amin</h1>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
};
},
methods: {
async getControllerTest() {
let res = await axios.get('https://localhost:7043/ControllerAuthorizationTest/ControllerTest_1');
console.log(res);
},
},
mounted() {
this.getControllerTest();
},
}
</script>
<style scoped lang="scss">
</style>
对以上功能更为具体实现和注释见:
1、230111_007shopDemo(Token的Postman、Swagger和Vue调试)
2、230111_008shopvue(Token的Postman、Swagger和Vue调试)