目录
jwt-cpp
jwt-cpp 是一个仅包含头文件的 C++11 库,用于创建和验证 JSON Web Tokens (JWT)。
Token
为什么需要Token
用户登录成功后, 需要反复到服务器获取敏感数据,服务器对每次请求都要验证是哪位用户发送的, 且用户是否合法, 需要反复查询数据库, 对数据库造成过大压力。
所以,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
Token验证流程
- 客户端使用用户名和密码请求登录(服务器–后端)
- 服务端收到请求,验证用户名和密码
- 验证成功后,服务端(后台)会生成一个token,然后把这个token发送给客户端(前端)
- 客户端收到token后把它存储起来,可以放在cookie或者Local Storage(本地存储)里
- 客户端每次向服务端发送请求的时候都需要带上服务端发给的token(客户端–前端)
- 服务端收到请求,然后去验证客户端请求里面带着token,如果验证成功,就向客户端返回请求的数据
这个token必须在每次请求时传递给服务器,应该保存在请求头里,另外服务器要支持CORS(跨域资源共享)的策略
JWT
JWT是什么
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。
JWT生成的Token有什么好处
安全性比较高,加上密匙加密而且支持多种算法。
携带的信息是自定义的,而且可以做到验证token是否过期。
验证信息可以由前端保存,后端不需要为保存token消耗内存。
JWT组成
一个JWT实际上就是一个字符串,它由三部分组成,头部(header)、载荷(payload)与签名(signature)。
头部(header)
头部用于描述关于该JWT的最基本的信息:类型(即JWT)以及签名所用的算法(如HMACSHA256或RSA)等。
将头部进行base64加密(该加密是可以对称解密的),构成了第一部分:
{
"alg": "HS256", //算法名字
"typ": "JWT" // 表示这是一个JWT
}
将其转换为JSON字符串:
{"alg":"HS256","typ":"JWT"}
然后进行Base64 URL编码,得到:
ew0KICJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
载荷(payload)
第二部分是载荷,就是存放用户id之类的信息的地方。
-
标准中注册的声明(建议但不强制使用)
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
-
公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. -
私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
然后将其进行base64加密,得到Jwt的第二部分:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
签名(signature)
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret(盐,一定要保密)
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行secret(密钥)组合加密,然后就构成了JWT的第三部分。
三部分JWT合起来(中间以.分割)构成最终JWT
服务器鉴权
- 服务器获取连接客户端携带的Token
- 对header和 payload 进行base64解码,得到具体的算法
- 然后通过HS256算法将 header 和 payload 和 密钥(在JWT中,密钥用于生成或验证Token的签名,确保其安全性和有效性)进行计算得出内容,让计算出的内容与Token中的第三部分,也就是Signature去比较,如果一致则验证通过,反之则失败。
生成Signature的算法如下:
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload);
HMACSHA256(encodedString, '!Q@#$%^%&');
Signature由 header 和 payload 经过 base64 编码后加 “盐”得到的。上面的 “盐”值为: “!Q@#$%^%&” ;
token过期校验
有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间。原因:从服务器发出的token,服务器自己并不做记录,就存在一个弊端:服务端无法主动控制某个token的立刻失效
//设置过期时间 1分钟
.setExpiration(new Date(System.currentTimeMillis() + 60 * 1000))
处理结果
鉴权通过: 如果Token验证通过,服务器将允许请求继续进行,并根据Token中的信息处理请求。
鉴权失败: 如果Token验证失败,服务器将拒绝请求,并返回相应的错误信息,如HTTP 401(未授权)或HTTP 403(禁止访问)。
JWT头部包含加密算法
加密解密
数据加密 的基本过程,就是对原来为 明文 的文件或数据按 某种算法 进行处理,使其成为 不可读 的一段代码,通常称为 “密文”。通过这样的途径,来达到 保护数据 不被 非法人窃取、阅读的目的。
加密 的 逆过程 为 解密,即将该 编码信息 转化为其 原来数据 的过程。
对称加密与非对称加密
其中对称加密算法的加密与解密 密钥相同,非对称加密算法的加密密钥与解密 密钥不同,此外,还有一类 不需要密钥 的 散列算法。
在 对称加密算法 中,使用的密钥只有一个,发送 和 接收 双方都使用这个密钥对数据进行 加密 和 解密。
在 非对称加密算法 中
如果使用 公钥 对数据 进行加密,只有用对应的 私钥 才能 进行解密。
如果使用 私钥 对数据 进行加密,只有用对应的 公钥 才能 进行解密。
常见的 对称加密 算法主要有 DES、3DES、AES 等,常见的 非对称算法 主要有 RSA、DSA 等,散列算法 主要有 SHA-1、MD5 等。
对称加密和非对称加密比较
对称加密和非对称加密是两种常见的加密方式,它们有以下几个方面的比较:
加密和解密速度:
- 对称加密:通常速度较快,因为使用相同的密钥进行加密和解密,计算量相对较小。
- 非对称加密:速度较慢,因为涉及到更复杂的数学运算。
密钥管理:
- 对称加密:密钥的分发和管理相对困难,因为通信双方需要共享相同的密钥,并且要确保密钥的安全传递。
- 非对称加密:密钥管理相对简单,公钥可以公开,私钥由所有者保密。
安全性:
- 对称加密:如果密钥泄露,整个通信的安全性就会受到威胁。
- 非对称加密:安全性更高,因为私钥不公开,即使公钥被获取,也难以破解加密信息。
应用场景:
- 对称加密:适用于大量数据的加密传输,如文件加密、数据库加密等。
- 非对称加密:常用于数字签名、密钥交换、身份验证等场景。
HMAC数据摘要/数据指纹
通常直接使用HMAC SHA256(对称加密算法),并且支持多种算法
HMAC 是密钥相关的 哈希运算消息认证码(Hash-based Message Authentication Code),HMAC 运算利用 哈希算法 (MD5、SHA1 等),以 一个密钥 和 一个消息 为输入,生成一个 消息摘要 作为 输出。
HMAC 发送方 和 接收方 都有的 key 进行计算,而没有这把 key 的第三方,则是 无法计算 出正确的 散列值的,这样就可以 防止数据被篡改。
数据摘要不是一种加密机制,因为它是没有解密这个过程的(不可逆),但它有一个很重要的功能,通过数据摘要来判断文本是否被篡改
具体操作可以用下面的图片来表示
利用Hash算法,从原始文本中提取固定大小的字符串,我们将这个字符串称作数据摘要,从它的另一个名字——数据指纹,我们也可以看出来,它是具有很强的唯一性
对称加密问题
对称加密密钥无法进行安全传递
采用:非对称加密+对称加密
第一步.服务器端将公钥S传给客户端
第二步.客户端对公钥S进行对称加密,在本地⽣成对称密钥C, 通过公钥S加密, 发送给服务器
第三步,服务器端利用私钥S’,获取到对应的对称密钥C,那以后双方进行交互的时候,都直接采用对称密钥C即可
后续客户端和服务器的通信都只⽤对称加密即可