jwt判断token是否过期_JWT原理
在前后端分离开发时为什么需要⽤户认证呢?原因是由于 HTTP 协定是不储存状态的(stateless),这意味着当我们透过帐号密码验证⼀个使⽤者时,当下⼀个 request 请求时它就把刚刚的资料忘了。于是我们的程序就不知道谁是谁,就要再验证⼀次。所以为了保证系统安全,我们就需要验证⽤户否处于登录状态。
传统⽅式
前后端分离通过 Restful API 进⾏数据交互时,如何验证⽤户的登录信息及权限。在原来的项⽬中,使⽤的是最传统也是最简单的⽅式,前端登录,后端根据⽤户信息⽣成⼀个token,并保存这个 token 和对应的⽤户 id 到数据库或 Session 中,接着把 token 传给⽤户,存⼊浏览器 cookie,之后浏览器请求带上这个 cookie,后端根据这个 cookie 值来查询⽤户,验证是否过期。
但这样做问题就很多,如果我们的页⾯出现了 XSS 漏洞,由于 cookie 可以被 JavaScript 读取,XSS 漏洞会导致⽤户 token 泄露,⽽作为后端识别⽤户的标识,cookie 的泄露意味着⽤户信息不再安全。尽管我们通过转义输出内容,使⽤ CDN 等可以尽量避免 XSS 注⼊,但谁也不能保证在⼤型的项⽬中不会出现这个问题。
在设置 cookie 的时候,其实你还可以设置 httpOnly 以及 secure 项。设置 httpOnly 后 cookie 将不能被 JS 读取,浏览器会⾃动的把它加在请求的 header 当中,设置 secure 的话,cookie 就只允许通过 HTTPS 传输。secure 选项可以过滤掉⼀些使⽤ HTTP 协议的 XSS 注⼊,但并不能完全阻⽌。
httpOnly 选项使得 JS 不能读取到 cookie,那么 XSS 注⼊的问题也基本不⽤担⼼了。但设置 httpOnly 就带来了另⼀个问题,就是很容易的被 XSRF,即跨站请求伪造。当你浏览器开着这个页⾯的时候,另⼀个页⾯可以很容易的跨站请求这个页⾯的内容。因为 cookie 默认被发了出去。
另外,如果将验证信息保存在数据库中,后端每次都需要根据token查出⽤户id,这就增加了数据库的查询和存储开销。若把验证信息保存在 session 中,有加⼤了服务器端的存储压⼒。那我们可不可以不要服务器去查询呢?如果我们⽣成token遵循⼀定的规律,⽐如我们使⽤对称加密算法来加密⽤户id形成token,那么服务端以后其实只要解密该token就可以知道⽤户的id是什么了。不过呢,我只是举个例⼦⽽已,要是真这么做,只要你的对称加密算法泄露了,其他⼈可以通过这种加密⽅式进⾏伪造token,那么所有⽤户信息都不再安全了。恩,那⽤⾮对称加密算法来做呢,其实现在有个规范就是这样做的,就是我们接下来要介绍的 JWT。
Json Web Token(JWT)
JWT 是⼀个开放标准(RFC 7519),它定义了⼀种⽤于简洁,⾃包含的⽤于通信双⽅之间以 JSON 对
象的形式安全传递信息的⽅法。JWT 可以使⽤ HMAC 算法或者是 RSA 的公钥密钥对进⾏签名。
JWT 特点
可以通过 URL, POST 参数或者在 HTTP header 发送,因此数据量⼩,传输速度快。
严格的结构化。它⾃⾝(在 payload 中)就包含了所有与⽤户相关的验证消息,如⽤户可访问路由、访问有效期等信息,服务器⽆需再去连接数据库验证信息的有效性,并且 payload ⽀持为你的应⽤⽽定制化。
⽀持跨域验证,可以应⽤于单点登录。
JWT 组成session如何设置和读取
text
Header 头部
头部包含了两部分:
1. 声明类型,这⾥是 jwt
2. 声明加密的算法 通常直接使⽤ HMAC SHA256
{
"alg": "HS256",
"typ": "JWT"
}
它会使⽤ Base64 编码组成 JWT 结构的第⼀部分,也就是说,它是可以被翻译回原来的样⼦来的。它并不是⼀种加密过程。如下图所⽰:
Payload 有效载荷
这部分就是我们存放信息的地⽅了,你可以把有效信息放在这⾥。这些有效信息分为三部分:
1. 标准中注册的声明
2. 公共的声明
3. 私有的声明
标准中注册的声明 (建议但不强制使⽤):
iss: 该 JWT 的签发者,⼀般是服务器,是否使⽤是可选的;
iat(issued at): 在什么时候签发的(UNIX时间),是否使⽤是可选的;
exp(expires): 什么时候过期,这⾥是⼀个Unix时间戳,是否使⽤是可选的;
aud: 接收该JWT的⼀⽅,是否使⽤是可选的;
sub: 该JWT所⾯向的⽤户,userid,是否使⽤是可选的;
其他还有:
nbf (Not Before):如果当前时间在nbf⾥的时间之前,则Token不被接受;⼀般都会留⼀些余地,⽐如⼏分钟,是否使⽤是可选的;
jti: jwt 的唯⼀⾝份标识,主要⽤来作为⼀次性 token,从⽽回避重放攻击。
公共的声明:
公共的声明可以添加任何的信息,⼀般添加⽤户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密。
私有的声明:
私有声明是提供者和消费者所共同定义的声明,⼀般不建议存放敏感信息,因为base64是对称解密的,
意味着该部分信息可以归类为明⽂信息。
// 包括需要传递的⽤户信息;
{ "iss": "Online JWT Builder",
"iat": 1416797419,
"exp": 1448333419,
"aud": "www.gusibi",
"sub": "uid",
"nickname": "goodspeed",
"username": "goodspeed",
"scopes": [ "admin", "user" ]
}
同样的,它会使⽤ Base64 编码组成 JWT 结构的第⼆部分
Signature 签名
前⾯两部分都是使⽤ Base64 进⾏编码的,即前端可以解开知道⾥⾯的信息。Signature 需要使⽤编码后的 header 和 payload 以及我们提供的⼀个密钥,然后使⽤ header 中指定的签名算法(HS256)进⾏签名。签名的作⽤是保证 JWT 没有被篡改过。
1. header (base64后的)
2. payload (base64后的)
3. secret
将这三部分⽤ . 连接成⼀个完整的字符串,构成了最终的 jwt:
最后⼀步签名的过程,实际上是对头部以及有效载荷内容进⾏签名,防⽌内容被窜改。如果有⼈对头
部以及有效载荷的内容解码之后进⾏修改,再进⾏编码,最后加上之前的签名组合形成新的 JWT 的话,那么服务器端会判断出新的头部和有效载荷形成的签名和 JWT 附带上的
签名是不⼀样的。如果要对新的头部和有效载荷进⾏签名,在不知道服务器加密时⽤的密钥的话,得出来的签名也是不⼀样的。这样就能保证token不会被篡改。
信息暴露
在这⾥⼤家⼀定会问⼀个问题:Base64 是⼀种编码,是可逆的,那么我的信息不就被暴露了吗?
是的。所以,在 JWT 中,不应该在有效载荷⾥⾯加⼊任何敏感的数据。在上⾯的例⼦中,我们传输的是⽤户的 User ID。这个值实际上不是什么敏感内容,⼀般情况下被知道也是安全的。但是像密码这样的内容就不能被放在 JWT 中了。如果将⽤户的密码放在了 JWT 中,那
么怀有恶意的第三⽅通过 Base64 解码就能很快地知道你的密码了。
因此 JWT 适合⽤于向 Web 应⽤传递⼀些⾮敏感信息。JWT 还经常⽤于设计⽤户认证和授权系统,甚⾄实现 Web 应⽤的单点登录。JWT 使⽤
1. ⾸先,前端通过 Web 表单将⾃⼰的⽤户名和密码发送到后端的接⼝。这⼀过程⼀般是⼀个 HTTP POST 请求。建议的⽅式是通过
SSL 加密的传输(https 协议),从⽽避免敏感信息被嗅探。
2. 后端核对⽤户名和密码成功后,将⽤户的 id 等其他信息作为 JWT Payload(有效载荷),将其与头部分别进⾏ Base64 编码拼接后签
名,形成⼀个 JWT。形成的 JWT 就是⼀个形同 的字符串。
3. 后端将 JWT 字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在 localStorage 或 sessionStorage 上,退出
登录时前端删除保存的 JWT 即可。
4. 前端在每次请求时将 JWT 放⼊ HTTP Header 中的 Authorization 位。(解决 XSS 和 XSRF 问题)
5. 后端检查是否存在,如存在验证 JWT 的有效性。例如,检查签名是否正确;检查 Token 是否过期;检查 Token 的接收⽅是否是⾃
⼰(可选)。
6. 验证通过后后端使⽤ JWT 中包含的⽤户信息进⾏其他逻辑操作,返回相应结果。
和 Session ⽅式存储 id 的差异
Session ⽅式存储⽤户 id 的最⼤弊病在于 Session 是存储在服务器端的,所以需要占⽤⼤量服务器内存,对于较⼤型应⽤⽽⾔可能还要保存许多的状态。⼀般⽽⾔,⼤型应⽤还需要借助⼀些 KV 数据库和⼀系列缓存机制来实现 Session 的存储。
⽽ JWT ⽅式将⽤户状态分散到了客户端中,可以明显减轻服务端的内存压⼒。除了⽤户 id 之外,还可以存储其他的和⽤户相关的信息,例如该⽤户是否是管理员、⽤户所在的分组等。虽说 JWT ⽅式让服务器有⼀些计算压⼒(例如加密、编码和解码),但是这些压⼒相⽐磁盘存储⽽⾔可能就不算什么了。具体是否采⽤,需要在不同场景下⽤数据说话。
单点登录
Session ⽅式来存储⽤户 id,⼀开始⽤户的 Session 只会存储在⼀台服务器上。对于有多个⼦域名的站点,每个⼦域名⾄少会对应⼀台不同的服务器,例如:www.taobao,nv.taobao,nz.taobao,login.taobao。所以如果要实现在login.taobao 登录后,在其他的⼦域名下依然可以取到 Session,这要求我们在多台服务器上同步 Session。使⽤ JWT 的⽅式则没有这个问题的存在,因为⽤户的状态已经被传送到了客户端。
优点
因为 json 的通⽤性,所以JWT是可以进⾏跨语⾔⽀持的,像 JAVA, JavaScript, NodeJS, PHP 等很多语⾔都可以使⽤。
因为有了 payload 部分,所以 JWT 可以在⾃⾝存储⼀些其他业务逻辑所必要的⾮敏感信息。
便于传输,jwt 的构成⾮常简单,字节占⽤很⼩,所以它是⾮常便于传输的。
它不需要在服务端保存会话信息, 所以它易于应⽤的扩展。
安全相关
不应该在 jwt 的 payload 部分存放敏感信息,因为该部分是客户端可解密的部分。
保护好 secret 私钥,该私钥⾮常重要。
如果可以,请使⽤ https 协议。
思考
假设给你这么⼀段 token
1. 你知道这个 token 有⼏部分组成吗?
2. 这个 token 每⼀部分是怎么编码的?
3. 这段token⾥背后到底包含了哪些信息呢?它想⼲什么呢?
4. 这个token是否值得我们信任呢?它有没有加密或签名过?有没有被更改过?
5. 我们如何往这个token⾥添加信息呢?可以添加哪些信息呢?
6. 假设这是个有效的token,它⼀旦签发出去,就相当于把锁的钥匙给了别⼈,就总是能打开门的,但我们如何让其失效掉呢?分别有哪
些有效⼿段呢?
7. JWT除了⽤来⽤户认证,还可以⼲些什么呢?
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论