JWT

 

JWT是什么?

JWT全称是Json Web Token,是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

JWT的结构

JWT一般由三段构成,用.号分隔开,第一段是header,第二段是payload,第三段是signature,例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTXJCdWciLCJleHAiOjE1MTI5NTkzMDMuMCwianRpIjoibHVvemhpcGVuZyJ9.9iwGMHmS0mophyFgliLK15hs_eE770IchaZ-bWcX5c0

1header

jwt的头部承载两部分信息:

声明类型。这里是jwt
声明加密的算法。通常直接使用 HMAC SHA256,其它还有RS256

完整的头部就像下面这样的JSON

  {

"alg": "HS256",

 "typ": "JWT"

}

 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 

2playload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:

  标准中注册的声明
  公共的声明
  私有的声明

标准中注册的声明 (建议但不强制使用)

    iss jwt签发者
    subjwt所面向的用户
    aud:接收jwt的一方
    expjwt的过期时间,这个过期时间必须要大于签发时间
    nbf:定义在什么时间之前,该jwt都是不可用的.
    iat jwt的签发时间
    jti  jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

 

公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个playload

{

    "name": "MrBug",

    "exp": 1512959303,

    "jti": "luozhipeng"

}

 然后将其进行base64加密,得到Jwt的第二部分

eyJuYW1lIjoiTXJCdWciLCJleHAiOjE1MTI5NTkzMDMuMCwianRpIjoibHVvemhpcGVuZyJ9 

 

3signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

    header (base64后的)
    payload (base64后的)
    secret

这个部分需要base64加密后的headerbase64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加secret组合加密,然后就构成了jwt的第三部分。

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret');

9iwGMHmS0mophyFgliLK15hs_eE770IchaZ-bWcX5c0

将这三部分用.连接成一个完整的字符串,构成了最终的jwt

1

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTXJCdWciLCJleHAiOjE1MTI5NTkzMDMuMCwianRpIjoibHVvemhpcGVuZyJ9.9iwGMHmS0mophyFgliLK15hs_eE770IchaZ-bWcX5c0

 
注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

 

整个流程就是这样的:

 

 

安全相关

不应该在jwtpayload部分存放敏感信息,因为该部分是客户端可解密的部分。

保护好secret私钥,该私钥非常重要。

如果可以,请使用https协议

为什么要用JWT?

一般在一个Web应用程序中,那么它通常使用一个cookie来表示一个已经登录的用户。一般的流程是:用户单击登录,进入登录页面,输入有效凭证后,服务器发送给用户浏览器的响应包含一个带有加密信息的Set-Cookie头。

cookie会被设置上domain 例如 xxx.com,每次浏览器向这个domain发送请求时,设置在这个domain上的cookie也会被带上。在服务器上,cookie将被解密,然后使用解密后的内容来创建用户的Identity。

如果客户端是一个浏览器,这种方式将会非常非常适合。不过当我们的客户端是一个移动应用程序时候,cookie机制将不再适用,所以我们要使用一种token机制来进行身份的认证,这里我们就可以使用JWT,优点如下:

1.跨语言使用。

2.服务器端无需再保存任何东西,只需要客户端保存token就可以。

3.实现简单。

4.统一认证方式,如果是移动端也要验证的话,jwt也支持就无需修改,否则客户端 服务器一套,移动端 服务器又是一套

NET中JWT怎么用

Nuget安装

 /// <summary>

 /// 获取token信息

 /// </summary>

 /// <param name="tokenModel"></param>

/// <returns></returns>

public static string issueJwt(TokenModel tokenModel)

{

//获取Appsetting配置信息

string iss = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });

 string aud = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" });

 string secret = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });

 

 

//构造payload

var claims = new List<Claim>

{
//nbf 生效时间 、Jti 编号、iat 签发时间、aud 受众、exp 过期时间

new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),

new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),

new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,

 //这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间

new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"),

new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(1000).ToString()),

new Claim(JwtRegisteredClaimNames.Iss,iss),

new Claim(JwtRegisteredClaimNames.Aud,aud),

};

 

 

//秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)

 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));

//加密方式

var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

 

var jwt = new JwtSecurityToken(

            issuer: iss,

            claims: claims,

            signingCredentials: creds);

 

//生成jwt token

var jwtHandler = new JwtSecurityTokenHandler();

var encodedJwt = jwtHandler.WriteToken(jwt);

 

return encodedJwt;

}

 

/// <summary>

/// 解析

/// </summary>

/// <param name="jwtStr"></param>

/// <returns></returns>

public static TokenModel SerializeJwt(string jwtStr)

{

var jwtHandler = new JwtSecurityTokenHandler();

JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);

object role;

 try

{

jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);

}

catch (Exception e)

{

Console.WriteLine(e);

  throw;

}

var tm = new TokenModel

{

     Uid = jwtToken.Id.ToString(),

     Role = role != null ? role.ToString() : "",

};

 return tm;

}

}

参考:

https://blog.csdn.net/sd7o95o/article/details/78852450

https://www.cnblogs.com/mrbug/p/8022826.html

https://www.jianshu.com/p/2342260c95ff

https://www.cnblogs.com/xiaojinFat/p/13345685.html

原文地址:https://www.cnblogs.com/zxking/p/13396937.html