维持用户会话状态的机制有 3 种:
维持用户会话状态的方案:cookie-session、JWT 等。
Cookie 是浏览器存储的方式之一。
Cookie 的特点:
key: value
的形式存储的。key 的名字是唯一的,相同名字时,后者会覆盖掉前者。Cookie 的适用场景(同时满足以下三个条件即可):
Web 程序是使用 HTTP 协议传输的,而 HTTP 协议是无状态的协议,对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。Cookie 就是用来解决这些问题的。
若浏览器没有禁用 Cookie,则其第一次发送请求到服务器时,服务器会生成 Cookie 并发送给浏览器,浏览器把 Cookie 以 key: value
的形式保存到客户端磁盘的一个文件中,之后,浏览器再次访问同一网站的服务器时会携带这个 Cookie,这样服务器就能通过 Cookie 中携带的数据来区分不同的用户了——这就好比:给每个客户端颁发一个通行证,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。
推荐阅读:
session 是服务端存储的一个对象。
session 的主要作用是:通过存储访问者的信息来保持用户会话状态。
Session 的特点:
30 分钟
,若有效期内客户端没有访问过该 session,服务器就认为该客户端已离线并删除该 session。常用的 session API | 描述 |
---|---|
getId() | 获取 sessionid |
invalidate() | 让 session 立刻失效 |
getAttribute(String key) | 根据 key 获取该 session 中的 value |
setAttribute(String key,Object value) | 往 session 中存放 key-value |
removeAttribute(Stringkey) | 根据 key 删除 session 中的 key-value |
getServletContext() | 得到 ServletContext |
setMaxInactiveInterval(time) / getMaxInactiveInterval() | 设置/获取 session 的最大有效时间 |
getCreationTime() | 获取 session 的创建的时间 |
getLastAccessedTime() | 获取 session 最后一次访问的时间 |
getSession() | 从 HttpServletRequest 中获取 session |
需求:实现“跨浏览器的会话跟踪”
分析:因为 cookie 在多个浏览器之间是共享的(但是不能跨域),所以可以将 sessionid 存在 cookie 中,再把 cookie 存入磁盘中,然后在其他浏览器中再次访问该服务器时,就会读取到 cookie 中的 sessionid,从而回到上次访问的页面了。
实现:
session.setMaxInactiveInterval(2*3600); // session 保存俩小时
Cookie cookie=new Cookie("JSESSIONID",session.getId()); // sessionid 放到 cookie 中
cookie.setMaxAge(2*3600); // 客户端的 cookie 也保存俩小时
cookie.setPath("/"); // cookie 作用范围设为整个项目
response.addCookie(cookie); // 给浏览器返回该 Cookie
当浏览器 Cookie 被禁用时可以重写 URL 来将 jsessionid(其存储的也就是 sessionid) 传给服务器。
重写 URL 的方式有以下 2 种:
可以使用 response 对象的 或 encodeRedirectURL() 方法来重写 URL:
这个问题需要从以下 2 个方面考虑:
推荐阅读:
Token 是在服务端产生的。
token 可以翻译成"令牌",本质上它是一个全局唯一的字符串,用来唯一识别一个客户端。它是在 cookie 和 session 的基础之上延伸出来的一种维持用户会话状态的机制。
token 的主要作用是:维持用户会话状态。
Token 的优点:
Token 的缺点:
token解决了session依赖于单个Web服务器的问题。单体应用时用户的会话信息保存在session中,session存在于服务器端的内存中,由于前前后后用户只针对一个web服务器,所以没啥问题。但是一到了web服务器集群的环境下(我们一般都是用Nginx做负载均衡,若是使用了轮询等这种请求分配策略),就会导致用户小a在A服务器登录了,session存在于A服务器中,但是第二次请求被分配到了B服务器,由于B服务器中没有用户小a的session会话,导致用户小a还要再登陆一次,以此类推。这样用户体验很不好。当然解决办法也有很多种,比如同一个用户分配到同一个服务处理、使用cookie保持用户会话信息等。
如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。
使用 token 的 3 种使用方式:
借助 JWT 第三方插件生成 token 的案例:
import JWT from 'jsonwebtoken';
import { tokenBaseInfo } from '../config/crontab.config'
/**
* 创建 token
* @param {params}={需要携带进token的数据}
*/
export const createToken = (params) => {
const { secret, expires } = tokenBaseInfo;
// 创建一个 token
const token = JWT.sign({
id: params.id,
timestamp: (new Date()).getTime()
}, secret, {
expiresIn: expires//到期时间(秒)
});
return token;
}
/**
* 解析 token
*/
export const resolveToken = (token, secret) => {
return new Promise((resolve, reject) => {
JWT.verify(token, secret, (error, data) => {
error ? reject(error) : resolve(data);
});
})
}
推荐阅读:
session 和 cookie 密切相关,二者都生成于服务器。session 存储在服务器,而服务器会将自己生成的 sessionId 发送给浏览器,浏览器以jsessionid: <sessionid>
的形式将其存储到 cookie 中。可见,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。
cookie 和 session 在实际运用中都是一起使用的。只不过在前端看来是 cookie,在后端看来是 session。所以可以将 cookie-session 归纳为一种 维持用户会话状态的方案。
Cookie | Session | |
---|---|---|
保存地址 | 客户端 | 服务端 |
声明周期 | 可以自己设置,默认到浏览器关闭 | 保存一定时间 |
存储内容 | 文本字符串 | 对象 |
存储大小 | <4KB | 没有限制 |
安全性 | 弱 | 强 |
应用场景 | 保存网站登录信息; 保存上次查看的页面; 浏览计数 | 网上商城的购物车; 保存用户登陆信息; 防止用户非法登陆。 |
JWT 是将认证后的数据保存在客户端,session 是保存在服务器端。
使用 JWT 替代 session 认证后,服务器不用维护 session,分布式环境下不需要单点存储 session。因为 JWT 的自解性,只要验证 JWT 是否合法就可以了。大大提升了系统的可扩展性,特别适用于微服务。
单纯的 JWT 无法实现 “踢人封号” 的需求。因为 JWT 是将认证后的数据保存在客户端,具有自解性。
那如果我们非要实现强制用户登出要怎么办呢?
可以采用类似oauth2.0协议中的做法,认证后颁布2个token,access token和refresh token。
JWT 的注销
jwt 一旦生成很难注销
清除 Cookie 但是服务端还会保存。
失效时间设置的短一点,让他自己失效。
【推荐文章】
【参考文章】