概念
JSON Web Token (JWT) 是一种开放标准协议,它定义了一种“紧凑”和“自包含”的方式,用于在各方之间作为JSON对象安全地传输信息。这个协议主要被用于分布式的单点登录场景,并且可以用跨域认证解决方案。
作用
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
基本原理
- 客户端使用账号和密码请求登录接口。
- 登录成功后服务器使用签名密钥生成JWT,并返回JWT给客户端。
- 客户端再次向服务端请求其他接口时,请求头会带上JWT。
- 服务器接收到JWT后验证签名的有效性,对客户端做出相应的响应。
组成
JWT由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
- Header
- Payload
- Signature
// JWT一般如下面一样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
/**
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 => Header
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 =>Payload
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ =>Signature
*/
header
jwt的头部(header)承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
{
'typ': 'JWT',
'alg': 'HS256'
}
然后将头部进行base64转换:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
{
'name': '测试',
'age': 22
}
然后将头部进行base64转换:
W29iamVjdCBPYmplY3Rd
signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
创建签名需要使用编码后的header和payload以及一个秘钥,组成的公式:编码后的header、编码后的payload、一个密钥(key)进行加密sign( base64UrlEncode(header) + “.” + base64UrlEncode(payload), key)
const crypto = require('crypto');
var header = {
'typ': 'JWT',
'alg': 'HS256'
}
var payload = {
'name': '测试',
'age': 22
}
var key = 'secretkey'
function sign(info, key) {
const hmac = crypto.createHmac('sha256', key);
hmac.update(info);
return hmac.digest('hex');
}
function base64UrlEncode(item) {
return Buffer.from(JSON.stringify(item).toString('base64'))
}
const signature = sign(base64UrlEncode(header) + '.' + base64UrlEncode(payload), key);
console.log(signature);
//177a0d5268f8ccd38ab4728e19f37d781435e19b1c9a451ab8d4f6df850417cc
注意:key是保存在服务器端的,jwt的签发生成是在服务器端的,key就是用来进行jwt的签发和jwt的验证,它是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个key, 那就意味着客户端是可以自我签发jwt了。
应用
一般是在请求头里加入Authorization
,并加上Bearer
标注:
fetch('api/user/1', {
headers: {
'Authorization': 'Bearer ' + token
}
})
总结
优点
- 可扩展性:JWT基于JSON标准,可以轻松地添加自定义声明,以满足不同的应用程序需求。
- 安全性:JWT使用数字签名或加密机制来验证令牌的真实性,防止伪造和篡改。此外,JWT不需要在服务器上存储会话信息,从而降低了被攻击的风险。
- 跨平台兼容性:JWT是基于标准的JSON格式,可以在不同的平台和编程语言之间进行交互,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
- 简洁性:JWT数据量小,传输速度快,可以通过URL,POST参数或者在HTTP header发送。
- 自包含:JWT的负载中包含了所有用户所需要的信息,避免了多次查询数据库。
- 易于跨语言应用:因为JWT是以JSON加密的形式保存在客户端的,所以它是跨语言的,原则上任何web形式都支持。
- 支持分布式微服务:不需要在服务端保存会话信息,特别适用于分布式微服务架构。
安全相关
- 不应在JWT的负载部分存储敏感信息:JWT的负载部分是可以被解密的,如果在此部分存储敏感信息,攻击者可能会通过解密JWT获取这些信息。因此,敏感信息(如密码、个人信息等)不应直接存储在JWT中,而应通过其他安全的方式(如数据库)存储。
- 保护好密钥:生成JWT需要一个密钥(用于签名JWT),这个密钥必须保密。一旦密钥泄露,任何人都可以创建有效的JWT,这可能导致安全问题。因此,必须采取适当的措施来保护这个密钥,比如将它存储在安全的环境变量中,或者使用硬件安全模块(HSM)来存储和生成密钥。
- 尽可能使用HTTPS:JWT在网络中传输时,如果没有使用安全的协议(如HTTPS)进行传输,可能会被拦截。因此,在传输JWT时,应尽可能使用HTTPS,以确保数据的安全传输。此外,即使在内部网络中,也应使用HTTPS,以防止内部人员通过嗅探器等方式拦截JWT。
- 不要在JWT中存储永久数据:因为JWT是存储在客户端的,如果JWT中的数据被篡改或者被恶意用户获取到,那么可能会被利用。
- 设置合适的过期时间:如果JWT的过期时间设置得太长,可能会增加被拦截和重放的风险;如果设置得太短,则可能会影响用户体验。因此,应根据具体的应用情况,设置合适的过期时间。
- 使用足够强大的加密算法:JWT可以使用各种加密算法进行签名和加密,包括但不限于HMAC SHA256、RSA等。应选择足够强大的加密算法,以确保JWT的安全性。