.net core webapi jwt 更为清爽的认证 ,续期很简单(2)
程序员文章站
2022-06-28 20:06:52
.net core webapi jwt 更为清爽的认证 后续:续期以及设置Token过期 续期: 续期的操作是在中间件中进行的,续期本身包括了前一个Token的过期加发放新的Token,所以在说续期前先说Token过期 在开始之前先增加相应的配置:续期间隔 和 续期携带给前端的新Token的Hea ......
.net core webapi jwt 更为清爽的认证 后续:续期以及设置token过期
续期: 续期的操作是在中间件中进行的,续期本身包括了前一个token的过期加发放新的token,所以在说续期前先说token过期
在开始之前先增加相应的配置:续期间隔 和 续期携带给前端的新token的head.jwtconfig同步修改
"jwt": { "issuer": "issuer", "audience": "audience", "secretkey": "abc", "lifetime": 20, //单位分钟 "renewaltime": 10, //单位分钟,token续期的时间间隔,10表示超过10分钟再次请求就续期 "validatelifetime": true, "headfield": "auth", //头字段 "retokenheadfield": "retoken", "prefix": "", //前缀 "ignoreurls": [ "/swagger/index.html", "/swagger/v1/swagger.json", "/auth/gettoken", "/auth/invalidatetoken" ] }
internal class jwtconfig { public string issuer { get; set; } public string audience { get; set; } /// <summary> /// 加密key /// </summary> public string secretkey { get; set; } /// <summary> /// 生命周期 /// </summary> public int lifetime { get; set; } /// <summary> /// 续期时间 /// </summary> public int renewaltime { get; set; } /// <summary> /// 是否验证生命周期 /// </summary> public bool validatelifetime { get; set; } /// <summary> /// 验证头字段 /// </summary> public string headfield { get; set; } /// <summary> /// 新token的head字段 /// </summary> public string retokenheadfield { get; set; } /// <summary> /// jwt验证前缀 /// </summary> public string prefix { get; set; } /// <summary> /// 忽略验证的url /// </summary> public list<string> ignoreurls { get; set; } }
1.设置token过期
首先在jwt.cs中增加静态属性
public static list<string> invalidatetokens = new list<string>();
然后添加 jwt中添加方法:
bool invalidatetoken(string token);
public bool invalidatetoken(string token) { if (!invalidatetokens.contains(token)) { invalidatetokens.add(token); } return true; }
修改jwt中gettoken的方法:
string gettoken(idictionary<string, string> clims,string oldtoken=null);
public string gettoken(idictionary<string, string> claims,string oldtoken=null) { list<claim> claimsall = new list<claim>(); foreach (var item in claims) { claimsall.add(new claim(item.key, item.value??"")); } var symmetrickey = convert.frombase64string(this._base64secret); var tokenhandler = new jwtsecuritytokenhandler(); var tokendescriptor = new securitytokendescriptor { issuer = _jwtconfig.issuer, audience = _jwtconfig.audience, subject = new claimsidentity(claimsall), notbefore = datetime.now, expires = datetime.now.addminutes(this._jwtconfig.lifetime), signingcredentials =new signingcredentials(new symmetricsecuritykey(symmetrickey), securityalgorithms.hmacsha256signature) }; var securitytoken = tokenhandler.createtoken(tokendescriptor); if (!string.isnullorempty(oldtoken))//执行旧token过期 { if (!invalidatetokens.contains(oldtoken)) { invalidatetokens.add(oldtoken); } } return tokenhandler.writetoken(securitytoken); }
修改: validatetoken
public bool validatetoken(string token, out dictionary<string, string> clims) { clims = new dictionary<string, string>(); if (invalidatetokens.contains(token)) { return false; } claimsprincipal principal = null; if (string.isnullorwhitespace(token)) { return false; } var handler = new jwtsecuritytokenhandler(); try { var jwt = handler.readjwttoken(token); if (jwt == null) { return false; } var secretbytes = convert.frombase64string(this._base64secret); var validationparameters = new tokenvalidationparameters { requireexpirationtime = true, issuersigningkey = new symmetricsecuritykey(secretbytes), clockskew = timespan.zero, validateissuer = true,//是否验证issuer validateaudience = true,//是否验证audience validatelifetime = this._jwtconfig.validatelifetime,//是否验证失效时间 validateissuersigningkey = true,//是否验证securitykey validaudience = this._jwtconfig.audience, validissuer = this._jwtconfig.issuer }; securitytoken securitytoken; principal = handler.validatetoken(token, validationparameters, out securitytoken); foreach (var item in principal.claims) { clims.add(item.type, item.value); } return true; } catch (exception ex) { console.writeline(ex.tostring()); return false; } }
紧接着在auth中增加接口
/// <summary> /// 强制token失效 /// </summary> /// <param name="token"></param> /// <returns></returns> [httppost] public iactionresult invalidatetoken(string token) { return new jsonresult(this._jwt.invalidatetoken(token)); }
//需要让当前token强制过期的时候,客户端调用 invalidatetoken 传入当前token就可以
2.续期:修改中间件:usejwtmiddleware
public class usejwtmiddleware { private readonly requestdelegate _next; private jwtconfig _jwtconfig =new jwtconfig(); private ijwt _jwt; public usejwtmiddleware(requestdelegate next, iconfiguration configration,ijwt jwt) { _next = next; this._jwt = jwt; configration.getsection("jwt").bind(_jwtconfig); } public task invokeasync(httpcontext context) { if (_jwtconfig.ignoreurls.contains(context.request.path)) { return this._next(context); } else { if (context.request.headers.trygetvalue(this._jwtconfig.headfield, out microsoft.extensions.primitives.stringvalues authvalue)) { var authstr = authvalue.tostring(); if (this._jwtconfig.prefix.length > 0) { authstr = authvalue.tostring().substring(this._jwtconfig.prefix.length+1, authvalue.tostring().length -(this._jwtconfig.prefix.length+1)); } if (this._jwt.validatetoken(authstr, out dictionary<string, string> clims)&&!jwt.invalidatetokens.contains(authstr)) { list<string> climskeys = new list<string>() { "nbf", "exp", "iat", "iss","aud" }; idictionary<string, string> renewaldic = new dictionary<string, string>(); foreach (var item in clims) { if (climskeys.firstordefault(o=>o==item.key) == null) { context.items.add(item.key, item.value); renewaldic.add(item.key, item.value); } } //验证通过的情况下判断续期时间 if (clims.keys.firstordefault(o => o == "exp") != null) { var start = new datetime(1970, 1, 1, 0, 0, 0, datetimekind.utc); var timespan = long.parse(clims["exp"]); var expdate = start.addseconds(timespan).tolocaltime(); var o = expdate - datetime.now; if (o.totalminutes < _jwtconfig.renewaltime) { //执行续期当前token立马失效 //var newtoken = this._jwt.gettoken(renewaldic, authstr);
//var newtoken=this._jwt.gettoken(renewaldic);//生成新token当前token仍可用,过期时间以lifetime设置为准 context.response.headers.add(_jwtconfig.retokenheadfield, newtoken); } } return this._next(context); } else { context.response.statuscode = 401; context.response.contenttype = "application/json"; return context.response.writeasync("{\"status\":401,\"statusmsg\":\"auth vaild fail\"}"); } } else { context.response.statuscode = 401; context.response.contenttype = "application/json"; return context.response.writeasync("{\"status\":401,\"statusmsg\":\"auth vaild fail\"}"); } } } }
本例中,当客户端获取token超过10分钟未超过20分钟的这个时间段如果再执行请求,那么服务端就会给head头上带上 retoken:newtoken
下次请求带着新token过来就可以