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使用的更多相关文章

  1. 【ASP.NET Core】体验一下 Mini Web API

    在上一篇水文中,老周给大伙伴们简单演示了通过 Socket 编程的方式控制 MPD (在树莓派上).按照计划,老周还想给大伙伴们演示一下使用 Web API 来封装对 MPD 控制.思路很 Easy, ...

  2. ASP.NET Web API 2基于令牌的身份验证

    基于令牌的认证 我们知道WEB网站的身份验证一般通过session或者cookie完成的,登录成功后客户端发送的任何请求都带上cookie,服务端根据客户端发送来的cookie来识别用户. WEB A ...

  3. 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 ...

  4. 使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API

    Hypermedia As The Engine Of Application State (HATEOAS) HATEOAS(Hypermedia as the engine of applicat ...

  5. WebApi官网学习记录---web api中的路由

    如果一条路由匹配,WebAPI选择controller和action通过如下方式: 1.找到controller,将"controller"赋值给{controller}变量 2. ...

  6. 【翻译】在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 ...

  7. 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 重点: 实现多级子目录的压缩, ...

  8. ASP.NET Core MVC 网站学习笔记

    ASP.NET Core MVC 网站学习笔记 魏刘宏 2020 年 2 月 17 日 最近因为” 新冠” 疫情在家办公,学习了 ASP.NET Core MVC 网站的一些知识,记录如下. 一.新建 ...

  9. 一起学ASP.NET Core 2.0学习笔记(一): CentOS下 .net core2 sdk nginx、supervisor、mysql环境搭建

    作为.neter,看到.net core 2.0的正式发布,心里是有点小激动的,迫不及待的体验了一把,发现速度确实是快了很多,其中也遇到一些小问题,所以整理了一些学习笔记: 阅读目录 环境说明 安装C ...

随机推荐

  1. POJ 3461 Oulipo KMP算法(模板)

    题意: 给两组字符串a和b,求a在b中出现的次数 关于KMP: 马拉车算法是处理回文串,而KMP是处理前后缀的相同字符串的最长长度. a | a | b | a | a | f | a | a 数组 ...

  2. Codeforces 1329C - Drazil Likes Heap(堆+贪心)

    题目链接 题意 给出一个高度为 h 的大根堆, 要求弹出其中若干个数后高度变为 g, 并且前后大根堆都是满二叉树. 问新的大根堆所有数之和的最小值, 并要给出一种弹出数的操作序列(节点序号). h, ...

  3. java中的PO VO DAO BO POJO

    一.PO:persistant object 持久对象,可以看成是与数据库中的表相映射的ava对象. 最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合PO中应该不包含任何对数据 ...

  4. vue+express+mongodb 实现 增删改查

    一.创建一个vue项目 用脚手架vue-cli搭建一个项目,数据请求用axios方式,写几个按钮用来调接口(vue这块不做多解释,不懂的可以先去官网学习vue-cli:https://cli.vuej ...

  5. NCEP CFSR数据下载

    一.简介 CFSR(Climate Forecast SystemReanalysis)再分析资料使用了 GEOS-5(Goddard EarthObserving System)大气模式与资料同化系 ...

  6. Django之环境安装

    什么是Django Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.许多成功的网站和APP都基于Django. Django是一个开放源代码的Web应用框架,由 ...

  7. 3-1. 基于epoll架构的视频采集端设计

    精通epoll架构 epoll:Linux中最优秀的多路复用机制! 与select .poll区别 1.select和poll没有太大区别,除了select有文件描述符限制(1024个).select ...

  8. 关于竞赛大佬常用的 static const auto _ = []() 用法解析

    前言 在刷Leetcode的时候发现很多运算速度极快的代码都有这一段,所以研究一下. static const auto _ = []() { ios::sync_with_stdio(false); ...

  9. CentOS 通过 expect 批量远程执行脚本和命令

    我们有时可能会批量去操作服务器,比如批量在服务器上上传某个文件,安装软件,执行某个命令和脚本,重启服务,重启服务器等,如果人工去一台台操作的话会特别繁琐,并浪费人力. 这时我们可以使用expect,向 ...

  10. Go gRPC教程-服务端流式RPC(三)

    前言 上一篇介绍了简单模式RPC,当数据量大或者需要不断传输数据时候,我们应该使用流式RPC,它允许我们边处理边传输数据.本篇先介绍服务端流式RPC. 服务端流式RPC:客户端发送请求到服务器,拿到一 ...