token
1.简介
一般是用户登录成功,服务器会颁发一个token并返回给前端。前端将token保持在cookie或者localStorage里面,然后每次请求是都会带上这个token,一般都带在请求头里面。
2.token的内容
一般token里面必须有的是:
(1)会话用户标识:比如userid。
(2)token的过期时间,如果想更完整一点,可以加上token的颁发者,签名等等。
3.token的生成算法
一般是由服务器端将token的主要内容,过期时间等等做非对称加密,然后进行签证算法(防止客户端更改),具体看后面Jwt
4.token校正
当服务器收到请求时,首先校正token,校正有二种不同的方式。
(1)token生成后保存在服务器端(redis或者其他速度比较快的缓存中)。
优点:可控性强,可以用这个用来做单点登录,比如用另一个地方登录,就remove掉之前的token。
缺点:实现麻烦一点,并且占服务器内存。
(2)token生成后服务器不负责保存只负责校验。
优点:大大的减轻了服务器的压力。实现起来相对简单些。
缺点:一旦颁发就不可控,只能让它自动过期。
Jwt
1.Jwt全面Json Web Token,算是一种token规范。
2.组成有三部分
(1)Header:头部,一般包括token的签名方式。
(2)PayLoad:负载,也就是具体有效部分。
(3)Signature:签名,将前两部分进行签名算法,防止被篡改。
实现方式,将Header部分和payLoad部分分别进行base64算法,然后用点号“.”隔开拼接,然后进行签名算法,
然后在将三部分拼接(点号隔开)就得到Jwt
注意:jwt默认是采用base64编码的,也就是说客户端也能解码得出具体内容,所以除非特殊情况,重要敏感字段一等不能放在token中。
下面是具体实现:
创建个Jwt类
public class Jwt
{
public static string SALT = "OXpcRP8jmCfMKumY";
/// <summary>
///
/// </summary>
/// <param name="ExraPayload">额外的信息</param>
/// <returns></returns>
public static string Create(Dictionary<string, object> ExraPayload)
{
var Header = new Dictionary<string, string>();
Header.Add("tp", "MD5");
var Payload = new Dictionary<string, object>();
//JWT 规定了7个官方字段,供选用。
Payload.Add("iss", "signBy"); //颁发人
Payload.Add("jti", Guid.NewGuid().ToString()); //jwt的id
Payload.Add("exp", System.DateTime.Now.AddMinutes(20));//过期时间
Payload.Add("nbf", System.DateTime.Now);//生效时间
Payload.Add("iat", System.DateTime.Now);//签发时间
Payload.Add("sub", "subject");//主题
Payload.Add("aud", "audience");//受众
foreach (var item in ExraPayload)
{
if (Payload.ContainsKey(item.Key))
{
throw new Exception($"{item.Key}键值已被占用 不能使用 ");
}
else
{
Payload.Add(item.Key, item.Value);
}
}
string base64Header = Base64Url(Newtonsoft.Json.JsonConvert.SerializeObject(Header));
string base64Payload = Base64Url(Newtonsoft.Json.JsonConvert.SerializeObject(Payload));
string tmp = base64Header + "." + base64Payload;
string sign = Md5(tmp + SALT);//加盐,重要
return base64Header + "." + base64Payload + "." + sign;
}
//校验是否合法,是否过期
public static bool Check(string token)
{
string base64Header = token.Split('.')[0];
string base64Payload = token.Split('.')[1];
string sign = token.Split('.')[2];
string tmp = base64Header + "." + base64Payload;
var signCheck = Md5(base64Header + "." + base64Payload + SALT);
if (signCheck != sign)
{
return false;
}
var dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlDecode(base64Payload));
if (Convert.ToDateTime(dic["exp"]) < System.DateTime.Now)
{
//过期了
return false;
}
return true;
}
//校验是否合法,是否过期
public static Dictionary<string, object> GetPayLoad(string token)
{
string base64Payload = token.Split('.')[1];
var dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(Base64UrlDecode(base64Payload));
return dic;
}
public static string Base64Url(string input)
{
//JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。
//Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。
string output = "";
byte[] bytes = Encoding.UTF8.GetBytes(input);
try
{
output = Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
catch (Exception e)
{
throw e;
}
return output;
}
public static string Base64UrlDecode(string input)
{
string output = "";
input = input.Replace('-', '+').Replace('_', '/');
switch (input.Length % 4)
{
case 2:
input += "==";
break;
case 3:
input += "=";
break;
}
byte[] bytes = Convert.FromBase64String(input);
try
{
output = Encoding.UTF8.GetString(bytes);
}
catch
{
output = input;
}
return output;
}
public static string Md5(string input, int bit = 16)
{
MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
byte[] hashedDataBytes;
hashedDataBytes = md5Hasher.ComputeHash(Encoding.GetEncoding("gb2312").GetBytes(input));
StringBuilder tmp = new StringBuilder();
foreach (byte i in hashedDataBytes)
{
tmp.Append(i.ToString("x2"));
}
if (bit == 16)
return tmp.ToString().Substring(8, 16);
else
if (bit == 32) return tmp.ToString();//默认情况
else return string.Empty;
}
}
使用方式:
public ActionResult LoginTokenCode(string username,string userpwd)
{
//todo 验证用户名密码正确
if (username!="sa" && userpwd!="123")
{
return View("LoginToken");
}
//在token中加入用户id,创建token
var dic = new Dictionary<string, object>();
dic.Add("Id", "qwe");
string token = Jwt.Create(dic);
//验证token是否正确是否过期
var isChecked = Jwt.Check(token);
return Content("成功!!");
}
更详细:http://www.mamicode.com/info-detail-2637888.html