Asp.Net Core 3.1 学习3、Web Api 中基于JWT的token验证及Swagger使用
1、初始JWT
1.1、JWT原理
JWT(JSON Web Token)是目前最流行的跨域身份验证解决方案,他的优势就在于服务器不用存token便于分布式开发,给APP提供数据用于前后端分离的项目。登录产生的 token的项目完全可以独立与其他项目。当用户访问登录接口的时候会返回一个token,然后访问其他需要登录的接口都会带上这个token,后台进行验证如果token是有效的我们就认为用户是正常登录的,然后我们可以从token中取出来一些携带的信息进行操作。当然这些携带的信息都可以通过其他额外的字段进行传递,但是用token传递的话,不用其他额外加其他字段了。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
1.2、JWT结构
JWT是由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpbklEIjoiYWRtaW4iLCJuYmYiOjE1ODc4OTE2OTMsImV4cCI6MTU4NzkyNzY5MywiaXNzIjoiV1lZIiwiYXVkIjoiRXZlcnlUZXN0T25lIn0.-snenNVHrrKq9obN8FzKe0t99ok6FUm5pHv-P_eYc30
第一部分我们称它为头部(header):声明类型,这里是jwt;声明加密的算法 通常直接使用 HMAC SHA256
{
'typ': 'JWT',
'alg': 'HS256'
}
第二部分我们称其为载荷(payload, 类似于飞机上承载的物品):
iss:Token发布者
exp:过期时间 分钟
sub:主题
aud:Token接受者
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
除以上默认字段外,我们还可以自定义私有字段,如下例:
{ "sub": "", "name": "wyy", "admin": true }
第三部分是签证(signature):这个部分需要base64加密后的header和base64加密后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。
2、生成Token
2.1、建立项目
在VS2019中新建一个Core Api程序 Core选3.1 然后在项目上添加一个Jwt文件夹帮助类,新建接口ITokenHelper,类:TokenHelper继承ITokenHelper,类JWTConfig,类TnToken
JWTConfig:用来保存读取jwt相关配置
/// <summary>
/// 配置token生成信息
/// </summary>
public class JWTConfig
{
/// <summary>
/// Token发布者
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// oken接受者
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 秘钥
/// </summary>
public string IssuerSigningKey { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public int AccessTokenExpiresMinutes { get; set; }
}
TnToken:存放Token 跟过期时间的类
/// <summary>
/// 存放Token 跟过期时间的类
/// </summary>
public class TnToken
{
/// <summary>
/// token
/// </summary>
public string TokenStr { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public DateTime Expires { get; set; }
}
ITokenHelper接口:token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
/// <summary>
/// token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
/// </summary>
public interface ITokenHelper
{
/// <summary>
/// 根据一个对象通过反射提供负载生成token
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="user"></param>
/// <returns></returns>
TnToken CreateToken<T>(T user) where T : class;
/// <summary>
/// 根据键值对提供负载生成token
/// </summary>
/// <param name="keyValuePairs"></param>
/// <returns></returns>
TnToken CreateToken(Dictionary<string, string> keyValuePairs);
}
TokenHelper:实现类
/// <summary>
/// Token生成类
/// </summary>
public class TokenHelper : ITokenHelper
{
private readonly IOptions<JWTConfig> _options;
public TokenHelper(IOptions<JWTConfig> options)
{
_options = options;
} /// <summary>
/// 根据一个对象通过反射提供负载生成token
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="user"></param>
/// <returns></returns>
public TnToken CreateToken<T>(T user) where T : class
{
//携带的负载部分,类似一个键值对
List<Claim> claims = new List<Claim>();
//这里我们用反射把model数据提供给它
foreach (var item in user.GetType().GetProperties())
{
object obj = item.GetValue(user);
string value = "";
if (obj != null)
value = obj.ToString(); claims.Add(new Claim(item.Name, value));
}
//创建token
return CreateToken(claims);
} /// <summary>
/// 根据键值对提供负载生成token
/// </summary>
/// <param name="keyValuePairs"></param>
/// <returns></returns>
public TnToken CreateToken(Dictionary<string, string> keyValuePairs)
{
//携带的负载部分,类似一个键值对
List<Claim> claims = new List<Claim>();
//这里我们通过键值对把数据提供给它
foreach (var item in keyValuePairs)
{
claims.Add(new Claim(item.Key, item.Value));
}
//创建token
return CreateTokenString(claims);
}
/// <summary>
/// 生成token
/// </summary>
/// <param name="claims">List的 Claim对象</param>
/// <returns></returns>
private TnToken CreateTokenString(List<Claim> claims)
{
var now = DateTime.Now;
var expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes));
var token = new JwtSecurityToken(
issuer: _options.Value.Issuer,//Token发布者
audience: _options.Value.Audience,//Token接受者
claims: claims,//携带的负载
notBefore: now,//当前时间token生成时间
expires: expires,//过期时间
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256));
return new TnToken { TokenStr = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
} }
2.2、在Startup中去配置jwt相关:
ConfigureServices中:
#region jwt配置
services.AddTransient<ITokenHelper, TokenHelper>();
//读取配置文件配置的jwt相关配置
services.Configure<JWTConfig>(Configuration.GetSection("JWTConfig"));
//启用JWT
services.AddAuthentication(Options =>
{
Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).
AddJwtBearer();#endregion
JwtBearerDefaults.AuthenticationScheme与AddJwtBearer();下载两个依赖即可。或者NuGet安装
appsettings中简单配置一下jwt相关的信息:
"JWTConfig": {
"Issuer": "WYY", //Token发布者
"Audience": "EveryTestOne", //Token接受者
"IssuerSigningKey": "WYY&YL889455200Sily", //秘钥可以构建服务器认可的token;签名秘钥长度最少16
"AccessTokenExpiresMinutes": "600" //过期时间 分钟
},
Configure中去启用验证中间件:
//启用认证中间件 要写在授权UseAuthorization()的前面
app.UseAuthentication();
2.3、一个简单的登录获取token
在Controllers文件夹里面新建一个api 名字LoginTest
[EnableCors("AllowCors")]
[Route("api/[controller]/[action]")]
[ApiController]
public class LoginTestController : ControllerBase
{
private readonly ITokenHelper tokenHelper = null;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="_tokenHelper"></param>
public LoginTestController(ITokenHelper _tokenHelper)
{
tokenHelper = _tokenHelper;
}
/// <summary>
/// 登录测试
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public ReturnModel Login([FromBody]UserDto user)
{
var ret = new ReturnModel();
try
{
if (string.IsNullOrWhiteSpace(user.LoginID) || string.IsNullOrWhiteSpace(user.Password))
{
ret.Code = ;
ret.Msg = "用户名密码不能为空";
return ret;
}
//登录操作 我就没写了 || 假设登录成功
if ( == )
{
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>
{
{ "loginID", user.LoginID }
};
ret.Code = ;
ret.Msg = "登录成功";
ret.TnToken= tokenHelper.CreateToken(keyValuePairs);
}
}
catch(Exception ex)
{
ret.Code = ;
ret.Msg = "登录失败:"+ex.Message;
}
return ret;
}
}
UserDto接收类
/// <summary>
/// 登录类Dto
/// </summary>
public class UserDto
{
/// <summary>
/// 用户名
/// </summary>
public string LoginID { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
}
ReturnModel 只是我自己封装的一个统一的接口返回格式标准
/// <summary>
/// 返回类
/// </summary>
public class ReturnModel
{
/// <summary>
/// 返回码
/// </summary>
public int Code { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 数据
/// </summary>
public object Data { get; set; }
/// <summary>
/// Token信息
/// </summary>
public TnToken TnToken { get; set; }
}
跨域上篇文章说了这里就不提了
2.4、前端获取token
我是用传统的MVC的一个启动页面
<input type="hidden" id="tokenValue" name="tokenValue" value="" />
<br /><br /><br />
<span>Token:</span><div id="txtval"></div><br />
<span>有效期:</span><div id="txtvalTime"></div><br /> <div>
<input type="button" value="获取Token" onclick="getToken()" /><br /><br /><br />
</div>
<script src="~/Scripts/jquery-3.3.1.js"></script>
<script type="text/javascript">
//获取token
function getToken() {
var data = JSON.stringify({ LoginID: "admin", Password: "admin888" });
$.ajax({
type: "post",
url: "https://localhost:44331/api/LoginTest/Login",
dataType: "json",
async: true,
data: data,
contentType: 'application/json',
success: function (data) {
console.log(data);
$("#txtval").html(data.tnToken.tokenStr);
$("#txtvalTime").html(new Date(data.tnToken.expires).Format("yyyy-MM-dd hh:mm"));
$("#tokenValue").val(data.tnToken.tokenStr); },
error: function (data) {
console.log("错误" + data);
}
});
}
Date.prototype.Format = function (fmt) { //author: zhengsh 2016-9-5
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
</script>
把Api启动起来 MVC也启动起来试试看
在JWT管网解码
3、验证前端传递的token
现在说说怎么来验证前台传递的jwt,其实很简单,最主要的就是验证token的有效性和是否过期。在接口ITokenHelper中添加验证的两个方法 。TokenHelper中实现
ITokenHelper中添加
/// <summary>
/// Token验证
/// </summary>
/// <param name="encodeJwt">token</param>
/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值</param>
/// <returns></returns>
bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null);
/// <summary>
/// 带返回状态的Token验证
/// </summary>
/// <param name="encodeJwt">token</param>
/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值</param>
/// <param name="action"></param>
/// <returns></returns>
TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action);
TokenHelper中添加
/// <summary>
/// 验证身份 验证签名的有效性
/// </summary>
/// <param name="encodeJwt"></param>
/// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>
public bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null)
{
var success = true;
var jwtArr = encodeJwt.Split('.');
if (jwtArr.Length < )//数据格式都不对直接pass
{
return false;
}
var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[]));
var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[]));
//配置文件中取出来的签名秘钥
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
success = success && string.Equals(jwtArr[], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[], ".", jwtArr[])))));
if (!success)
{
return success;//签名不正确直接返回
} //其次验证是否在有效期内(也应该必须)
var now = ToUnixEpochDate(DateTime.UtcNow);
success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())); //不需要自定义验证不传或者传递null即可
if (validatePayLoad == null)
return true; //再其次 进行自定义的验证
success = success && validatePayLoad(payLoad); return success;
}
/// <summary>
/// 时间转换
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
private long ToUnixEpochDate(DateTime date)
{
return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(, , , , , , TimeSpan.Zero)).TotalSeconds);
}
/// <summary>
///
/// </summary>
/// <param name="encodeJwt"></param>
/// <param name="validatePayLoad"></param>
/// <param name="action"></param>
/// <returns></returns>
public TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action)
{
var jwtArr = encodeJwt.Split('.');
if (jwtArr.Length < )//数据格式都不对直接pass
{
return TokenType.Fail;
}
var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[]));
var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[]));
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
if (!string.Equals(jwtArr[], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[], ".", jwtArr[]))))))
{
return TokenType.Fail;
}
//其次验证是否在有效期内(必须验证)
var now = ToUnixEpochDate(DateTime.UtcNow);
if (!(now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())))
{
return TokenType.Expired;
} //不需要自定义验证不传或者传递null即可
if (validatePayLoad == null)
{
action(payLoad);
return TokenType.Ok;
}
//再其次 进行自定义的验证
if (!validatePayLoad(payLoad))
{
return TokenType.Fail;
}
//可能需要获取jwt摘要里边的数据,封装一下方便使用
action(payLoad);
return TokenType.Ok;
}
其中TokenType是返回类型成功失败
public enum TokenType
{
Ok,
Fail,
Expired
}
在api LoginTest中新增两个验证的方法
/// <summary>
/// 验证Token
/// </summary>
/// <param name="tokenStr">token</param>
/// <returns></returns>
[HttpGet]
public ReturnModel ValiToken(string tokenStr)
{
var ret = new ReturnModel
{
TnToken = new TnToken()
};
bool isvilidate = tokenHelper.ValiToken(tokenStr);
if(isvilidate)
{
ret.Code = ;
ret.Msg = "Token验证成功";
ret.TnToken.TokenStr = tokenStr;
}
else
{
ret.Code = ;
ret.Msg = "Token验证失败";
ret.TnToken.TokenStr = tokenStr;
}
return ret;
}
/// <summary>
/// 验证Token 带返回状态
/// </summary>
/// <param name="tokenStr"></param>
/// <returns></returns>
[HttpGet]
public ReturnModel ValiTokenState(string tokenStr)
{
var ret = new ReturnModel
{
TnToken = new TnToken()
};
string loginID = "";
TokenType tokenType = tokenHelper.ValiTokenState(tokenStr, a => a["iss"] == "WYY" && a["aud"] == "EveryTestOne", action => { loginID = action["loginID"]; });
if (tokenType == TokenType.Fail)
{
ret.Code = ;
ret.Msg = "token验证失败";
return ret;
}
if (tokenType == TokenType.Expired)
{
ret.Code = ;
ret.Msg = "token已经过期";
return ret;
} //..............其他逻辑
var data = new List<Dictionary<string, string>>();
var bb = new Dictionary<string, string>
{
{ "Wyy", "" }
};
data.Add(bb);
ret.Code = ;
ret.Msg = "访问成功!";
ret.Data =data ;
return ret;
}
上面一个简单的验证和支持自定义验证的就写好了。下面带有状态的是让我们清楚的知道是什么状态请求登录的时候 或者请求数据的时候,是token过期还是说token没有获取到等等。
ValiTokenState第三个参数我还更了一个系统委托,是这样想的,处理可以验证token,还可以顺便取一个想要的数据,当然其实这样把相关逻辑混到一起也增加代码的耦合性,当时可以提高一点效率不用在重新解析一次数据,当然这个数据也可以通前台传递过来,所以怎么用还是看实际情况,这里只是封装一下提供这样一个方法,用的时候也可以用。
其前端请求代码
$.ajax({
type: "post",
url: "https://localhost:44331/api/LoginTest/ValiToken?tokenStr="+ $("#tokenValue").val(),
dataType: "json",
async: true,
data: { token: $("#tokenValue").val() },
contentType: 'application/json',
success: function (data) {
console.log(data);
},
error: function (data) {
console.log("错误" + data);
}
});
4、Api中过滤器实现通用token验证
项目上新建一个文件夹Filter,在文件夹Filter里新建一个过滤器TokenFilter
namespace JWTToken.Filter
{
public class TokenFilter : Attribute, IActionFilter
{
private ITokenHelper tokenHelper;
public TokenFilter(ITokenHelper _tokenHelper) //通过依赖注入得到数据访问层实例
{
tokenHelper = _tokenHelper;
} public void OnActionExecuted(ActionExecutedContext context)
{ }
public void OnActionExecuting(ActionExecutingContext context)
{
ReturnModel ret = new ReturnModel();
//获取token
object tokenobj = context.ActionArguments["token"]; if (tokenobj == null)
{
ret.Code = ;
ret.Msg = "token不能为空";
context.Result = new JsonResult(ret);
return;
} string token = tokenobj.ToString(); string userId = "";
//验证jwt,同时取出来jwt里边的用户ID
TokenType tokenType = tokenHelper.ValiTokenState(token, a => a["iss"] == "WYY" && a["aud"] == "EveryTestOne", action => { userId = action["userId"]; });
if (tokenType == TokenType.Fail)
{
ret.Code = ;
ret.Msg = "token验证失败";
context.Result = new JsonResult(ret);
return;
}
if (tokenType == TokenType.Expired)
{
ret.Code = ;
ret.Msg = "token已经过期";
context.Result = new JsonResult(ret);
}
if (!string.IsNullOrEmpty(userId))
{
//给控制器传递参数(需要什么参数其实可以做成可以配置的,在过滤器里边加字段即可)
//context.ActionArguments.Add("userId", Convert.ToInt32(userId));
}
}
}
}
context.ActionArguments。这是前段请求的时候地址栏带上的参数 token=xxx;这种类型的,不是请求的参数 不然会报错;
把过滤器在startup中注入一下:
services.AddScoped<TokenFilter>();
需要验证token的地方,直接加上这个过滤器即可
前台试试 请求上图的GetList
<input type="hidden" id="tokenValue" name="tokenValue" value="" />
<br /><br /><br />
<span>Token:</span><div id="txtval"></div><br />
<span>有效期:</span><div id="txtvalTime"></div><br /> <div>
<input type="button" value="获取Token" onclick="getToken()" /><br /><br /><br />
</div>
<input type="button" value="获取List" onclick="getList()" /><br />
<script src="~/Scripts/jquery-3.3.1.js"></script>
<script type="text/javascript">
//获取token
function getToken() {
var data = JSON.stringify({ LoginID: "admin", Password: "admin888" });
$.ajax({
type: "post",
url: "https://localhost:44331/api/LoginTest/Login",
dataType: "json",
async: true,
data: data,
contentType: 'application/json',
success: function (data) {
console.log(data);
$("#txtval").html(data.tnToken.tokenStr);
$("#txtvalTime").html(new Date(data.tnToken.expires).Format("yyyy-MM-dd hh:mm"));
$("#tokenValue").val(data.tnToken.tokenStr); },
error: function (data) {
console.log("错误" + data);
}
});
}
//获取list
function getList() {
var data = JSON.stringify();
$.ajax({
type: "post",
url: "https://localhost:44331/api/Home/GetList?token="+ $("#tokenValue").val(),
dataType: "json",
async: true,
data: { token: $("#tokenValue").val() },
contentType: 'application/json',
success: function (data) {
console.log(data);
$("#txtval").html(JSON.stringify(data)); },
error: function (data) {
console.log("错误" + data);
}
});
}
Date.prototype.Format = function (fmt) { //author: zhengsh 2016-9-5
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
</script>
现获取token 赋值在隐藏框里在请求
5、在Api中使用Swagger
5.1项目中添加Swagger的相关包
5.2ConfigureServices、Configure 中添加
#region Swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "测试接口文档",
Description = "测试接口"
});
// 为 Swagger 设置xml文档注释路径
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.DocInclusionPredicate((docName, description) => true);
//添加对控制器的标签(描述)
c.DocumentFilter<ApplyTagDescriptions>();//显示类名
c.CustomSchemaIds(type => type.FullName);// 可以解决相同类名会报错的问题
//c.OperationFilter<AuthTokenHeaderParameter>();
});
#endregion
app.UseSwagger(c =>
{
c.RouteTemplate = "swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Web App v1");
c.RoutePrefix = "doc";//设置根节点访问
//c.DocExpansion(DocExpansion.None);//折叠
c.DefaultModelsExpandDepth(-);//不显示Schemas
});
5.3、项目属性修改
5.4、添加接口类的注释
看效果
6、总结
JWT个人的理解就是api配置文件的IssuerSigningKey作为秘钥来加密的,客户端登录后获取到token 地址栏请求传到后端 后端通过解码获取到IssuerSigningKey是否跟后台解析出来的一直来匹配。后端可以卸载锅炉器里面来接收这个token来验证从而限制能不能访问Api。前端可以自己封装一个请求把token穿进去的参数就可以避免每次输入Token,前端可以Session?
下了班写的仓促了 哈哈。欢迎补充。
原文链接:https://www.cnblogs.com/w5942066/p/12781397.html
Asp.Net Core 3.1 学习3、Web Api 中基于JWT的token验证及Swagger使用的更多相关文章
- 【ASP.NET Core】体验一下 Mini Web API
在上一篇水文中,老周给大伙伴们简单演示了通过 Socket 编程的方式控制 MPD (在树莓派上).按照计划,老周还想给大伙伴们演示一下使用 Web API 来封装对 MPD 控制.思路很 Easy, ...
- ASP.NET Web API 2基于令牌的身份验证
基于令牌的认证 我们知道WEB网站的身份验证一般通过session或者cookie完成的,登录成功后客户端发送的任何请求都带上cookie,服务端根据客户端发送来的cookie来识别用户. WEB A ...
- ASP.NET Core 中文文档 第二章 指南(2)用 Visual Studio 和 ASP.NET Core MVC 创建首个 Web API
原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio 作者:Mike Wasson 和 Rick Anderso ...
- 使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API
Hypermedia As The Engine Of Application State (HATEOAS) HATEOAS(Hypermedia as the engine of applicat ...
- WebApi官网学习记录---web api中的路由
如果一条路由匹配,WebAPI选择controller和action通过如下方式: 1.找到controller,将"controller"赋值给{controller}变量 2. ...
- 【翻译】在Visual Studio中使用Asp.Net Core MVC创建第一个Web Api应用(二)
运行应用 In Visual Studio, press CTRL+F5 to launch the app. Visual Studio launches a browser and navigat ...
- C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...
- ASP.NET Core MVC 网站学习笔记
ASP.NET Core MVC 网站学习笔记 魏刘宏 2020 年 2 月 17 日 最近因为” 新冠” 疫情在家办公,学习了 ASP.NET Core MVC 网站的一些知识,记录如下. 一.新建 ...
- 一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx、supervisor、mysql环境搭建
作为.neter,看到.net core 2.0的正式发布,心里是有点小激动的,迫不及待的体验了一把,发现速度确实是快了很多,其中也遇到一些小问题,所以整理了一些学习笔记: 阅读目录 环境说明 安装C ...
随机推荐
- Java基础语法(11)-面向对象之关键字
title: Java基础语法(11)-面向对象之关键字 blog: CSDN data: Java学习路线及视频 1.this this是什么 它在方法内部使用,即这个方法所属对象的引用: clas ...
- c++中的多态机制
目录 1 背景介绍 2 多态介绍 2-1 什么是多态 2-2 多态的分类 2-3 动态多态成立的条件 2-4 静态联编和动态联编 2-5 动态多态的实现原理 2-6 虚析构函数 ...
- 创建Windows10无人值守(自动应答文件)教程
一.准备工作 系统要求: Windows10 1809版本 工具下载: 镜像:Windows10,任何一个版本都可以,我使用的是1909版本 ed2k://|file|cn_windows_10_bu ...
- B - Bound Found POJ - 2566(尺取 + 对区间和的绝对值
B - Bound Found POJ - 2566 Signals of most probably extra-terrestrial origin have been received and ...
- 学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?
在编程语言中有两个很基础的概念,即方法(method)和函数(function).如果达到了编程初级/入门级水平,那么你肯定在心中已有了初步的答案. 也许在你心中已有答案了 除去入参.返回值.匿名函数 ...
- python编程学习路线及笔记
话不多说,直接上图! 关于人工智能算法学习思路,欢迎浏览我的另一篇随笔:如果你想开始学习算法,不妨先了解人工智能有哪些方向? 之后博主将持续分享各大算法的学习思路和学习笔记:hello world: ...
- Spring ApplicationContext 容器
Spring ApplicationContext 容器 Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文. Application Con ...
- 为什么scanf(" %c",&c)中%c前要空格?
空格确实不是必须的,但有了空格就可以忽略你输入的空格. ****例如:scanf(" %c" ,&c),你输入了' a'(a前面有个空格),a就能被c接受. 但控制符前如果 ...
- CAP定理和BASE理论
CAP定理和BASE理论 标签(空格分隔): 操作系统 CAP定理 CAP定理: 一个分布式系统最多只能满足一致性 (Consistency), 可用性(Availability)和分区容错性(Par ...
- 国内 Java 开发者必备的两个装备,你配置上了么?
虽然目前越来越多的国产优秀技术产品走出了国门,但是对于众领域的开发者来说,依然对于国外的各种基础资源依赖还是非常的强.所以,一些网络基本技能一直都是我们需要掌握的,但是速度与稳定性问题一直也都有困扰着 ...