SpringBootWeb 篇-深入了解会话技术与会话跟踪三种技术(Cookie 会话跟踪、Session 会话跟踪与 JWT 令牌会话跟踪)
🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
文章目录
1.0 会话技术
2.0 会话跟踪
2.1 会话跟踪 - Cookie
2.1.1 客户端获取 Cookie 的流程
2.1.2 Cookie 会话跟踪的特点
2.2 会话跟踪 - Session
2.2.1 客户端获取 SESSIONID 的过程
2.2.2 Session 会话跟踪的特点
2.2.3 Session 会话跟踪技术与 Cookie 会话跟踪技术的区别
2.3 会话跟踪 - JWT 令牌技术
2.3.1 客户端获取到令牌的过程
2.3.2 生成 JWT 令牌与校验 JWT令牌
1.0 会话技术
用户打开浏览器,访问 web 服务器的资源,会话建立,直到有一方断开,会话结束。在一次会话中可以包含多次请求和响应。
2.0 会话跟踪
一种维护浏览器状态的方法,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
1)客户端会话跟踪技术:Cookie
2)服务端会话跟踪技术:Session
3)JWT 令牌技术
2.1 会话跟踪 - Cookie
Cookie 包含有关用户访问过的网站或应用程序的信息,以及用户在该网站或应用程序上的活动和偏好设置。通过使用 Cookie ,网站或应用程序可以识别用户并跟踪他们的活动,为用户提供个性化的服务和体验。
Cookie 可以存储各种信息,例如用户登录凭据、购物车内容、偏好设置等。网站或应用程序可以根据 Cookie 中的信息,实现记住用户登录状态、保存用户偏好设置等功能。
需要注意的是,Cookie 只能存储有限的信息,并且用户可以选择在浏览器设置中禁用 Cookie 或清除已存储的 Cookie 。
2.1.1 客户端获取 Cookie 的流程
首先客户端第一次发送请求到服务端,当服务端进行响应的时候就会在响应头中的字段 set-Cookie 设置一个 value 值响应给客户端,客户端收到该字段之后,就会在客户端本地上进行存储。当客户端继续向服务端发送每一个请求时,请求头上都会带有 Cookie 字段而该值就是之前服务端发送给客户端的值也就是客户端存储在本地上的 value 值。
这样服务端就可以轻松识别该请求是否来自同一个浏览器,若是来自同一个浏览器,就可以进行数据共享了。
代码模拟实现服务端设置 Set-Cookie、客户端自动存储 Cookie 且之后的每一次请求都会自动携带 Cookie 字段与该 value 值:
import org.example.Pojo.Result; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @RestController public class MyCookie { //当客户端第一次发送请求时,服务器响应头中响应一个 Set-Cookie 字段 @GetMapping("/getCookie") public Result getCookie(HttpServletResponse response){ response.addCookie(new Cookie("getCookie","MyCookie")); return Result.success(); } //当客户端后续发送给请求时,请求头中都会自动携带 Cookie 字段 @GetMapping("/takeCookie") public Result takeCookie(HttpServletRequest request){ String s = request.getHeader("Cookie"); System.out.println(s); return Result.success(); } }
启动程序,当客户端第一次发送请求时,服务端会响应一个 Set-Cookie 字段:
当客户端再次发送请求时,请求头就会自动携带 Cookie 字段与该 value 值:
2.1.2 Cookie 会话跟踪的特点
优点:HTTP 协议中支持的技术。
缺点:
1)移动端 APP 无法使用 Cookie 。
2)不安全,用户可以随意禁用 Cookie 。用户一旦禁用了,那么该会话跟踪技术就会失效了。
3)Cookie 不能跨域。从三个角度来判断是否跨域:协议、主机、端口号。
2.2 会话跟踪 - Session
Session 跟踪技术是在 Cookie 跟踪技术上进行升级,对于 Session 会话跟踪技术来说,数据存储在服务器端。而客户端只是存储的是 JSESSIONID 字段,因此数据在客户端中是无法查看的,只能由服务端根据 JSESSIONID 来映射到对应的数据从而进行查看。
2.2.1 客户端获取 SESSIONID 的过程
当客户端第一次发送请求到服务器,服务器就会自动创建 Session 对象,并生成一个唯一的JSESSIONID 来标识这个 Session 对象。将该对象存储在当前的服务器中,当服务端响应时,响应头中的字段 Set-Cookie 就会携带 JSESSIONID 值响应给客户端,客户端接收到 JSESSIONID 之后,会自动存储在本地,注意的是,存储的不是数据,而仅仅是类似 id 的值。
接着,客户端之后的每一个请求都自动携带 Cookie 字段与对应的 id 值。
随后,每次客户端发送请求时,会自动携带这个存储的 JSESSIONID 作为 Cookie 字段传递给服务器,服务器可以通过这个 JSESSIONID 来定位到对应的 Session 对象,实现会话的跟踪和管理。这样,在整个会话过程中,服务器和客户端可以通过 JSESSIONID 保持会话状态,实现数据的存储和传递。
代码模式实现以上过程:
import org.example.Pojo.Result; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @RestController public class MyCookie { //Session 跟踪技术 @GetMapping("getSession") public Result getSession(HttpSession session){ session.setAttribute("getSession","xbs"); //此时就会自动响应一个JSESSIONID给客户端 return Result.success(); } @GetMapping("takeSession") public Result takeSession(HttpServletRequest request){ //客户端再次发送请求时,就会携带一个JSESSIONID给服务器 //接着服务端就会根据该JSESSIONID来映射一个唯一的session对象, //该session对象一直存储在服务器中。 HttpSession session = request.getSession(); //从session中获取数据 Object get = session.getAttribute("getSession"); return Result.success(get); } }
客服端第一次发送请求:
客户端之后发送的请求:
2.2.2 Session 会话跟踪的特点
优点:
1)存储在服务端安全,数据在用户端是看不到的,用户端获取的是 id 仅此而已。
2)HTTP 协议中支持的技术。
缺点:
1)Cookie 不能跨域。
2)移动端 APP 无法使用 Cookie 。
3)服务器集群环境下无法直接使用 Session 。
2.2.3 Session 会话跟踪技术与 Cookie 会话跟踪技术的区别
1)存储位置:Cookie 会话跟踪技术是在客户端存储用户会话信息,而 Session 会话跟踪技术是在服务器端存储用户会话信息。
2)安全性:由于 Cookie 存储在客户端,因此 Cookie 会话跟踪技术可能存在一些安全风险,例如被盗取或篡改。相比之下,Session 会话跟踪技术在服务器端存储,相对更加安全。
3)生命周期:Cookie 会话跟踪技术的会话信息在浏览器关闭后仍然存在,可以通过设置 Cookie 的 expires 属性控制其有效期。而 Session 会话跟踪技术的会话信息只在用户在网站上活动时存在,在用户关闭浏览器后会话信息会被删除。
4)存储容量:Cookie 存储在客户端,因此其存储容量有限,通常为 4 KB。而 Session 存储在服务器端,理论上可以存储更多的会话信息。
2.3 会话跟踪 - JWT 令牌技术
JWT(JSON Web Token),定义了一种简洁的只包含的格式,用于在通信双方以 json 数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。可以简单的理解成字符串。
JWT 的组成:
1)第一部分:Header,记录令牌类型、签名算法。例如:"{"alg":"HS256","type":"JWT"}"
2)第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。
3)第三部分:Signature(签名),防止 Token 被篡改、确保安全性。将 header 、payload,并加入指定密钥,通过指定签名算法计算而来。
JWT 格式:
对于第一部分与第二部分在传输之前会被 Base64 编码方式进行编码,将 JSON 格式的字符串转化为基于 64 个可打印字符来表示。需要注意的是,这一部分相当于明码,没有进行加密处理,只是进行编码。
对于第三部分来说,将前面的两个部分进行了校验和之后,用私钥进行加密处理了,具体用什么密钥,取决于 Header 中的签名算法决定。
JWT 令牌特点:
1)JWT 令牌是无状态的。
2)支持 PC 端、移动端。
3)解决集群环境下的认证问题。
4)减轻服务器存储压力。
2.3.1 客户端获取到令牌的过程
当客户端第一次发送请求,服务端就会创建令牌,创建令牌由程序员决定,比如说,令牌中的签名算法、有效载荷中的内容是什么等都是自定义的。当客户端符合要求之后,服务端就会发送令牌给到客户端。
后续每一个请求,都要携带 JWT 令牌,系统在每次请求处理之前,先校验令牌(对第一、二部分用相同的方式进行校验和,得出来的数据与对第三部分的数据进行解码之后的数据进行比较,如果相同,那么数据是安全的没有被篡改;如果不相同,那么数据是不安全的,可能被篡改了),通过后,再处理。
2.3.2 生成 JWT 令牌与校验 JWT令牌
1)引入 JWT 依赖
io.jsonwebtoken jjwt 0.9.1 javax.xml.bind jaxb-api 2.3.1
2)生成 JWT 令牌
Jwts.builder() :创建一个 JWT 构建器对象。
signWith(SignatureAlgorithm, key):设置签名算法和密钥来对 JWT 进行签名。
addClaims(Map claims):添加载荷中的内容,参数是一个 Map 类型,用键值对的形式存放数据。
setExpiration(expiration) :设置 JWT 令牌的有效时间。
compact():生成最终的 JWT 令牌字符串。
除了上述方法外,还有其他方法可用于设置 JWT 的发行者、主题、ID 等信息,以及添加自定义的头部信息等。通过这些方法的组合,可以灵活地定制生成符合需求的 JWT 令牌。
签名算法的类型:
HS256:HMAC SHA-256,使用对称密钥进行 HMAC 签名。
HS512:HMAC SHA-512,使用对称密钥进行 HMAC 签名。
RS256:RSA SHA-256,使用 RSA 非对称密钥进行签名。
RS512:RSA SHA-512,使用 RSA 非对称密钥进行签名。
ES256:ECDSA SHA-256,使用椭圆曲线数字签名算法进行签名。
ES512:ECDSA SHA-512,使用椭圆曲线数字签名算法进行签名。
3)校验 JWT 令牌
Jwts.parser():用于创建一个 JWT 解析器对象,用于解析和验证 JWT 令牌。
setSigningKey(key):设置用于验证签名的密钥。
parseClaimsJwts(JWT 令牌):来解析具体的 JWT 令牌,返回一个 Claims 对象,该对象包含了 JWT 的所有信息。
getBody():用于获取 JWT 中载荷部分的内容,也就是 JWT 中存储的数据信息。
代码演示:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.Map; public class JWT { private static final String password = "test"; private static final int time = 3600 * 1000; //生成令牌 public static String getJWT(Map map){ return Jwts.builder().signWith(SignatureAlgorithm.HS256,password) .addClaims(map).setExpiration(new Date(System.currentTimeMillis() + time)) .compact(); } //解析令牌 public static Claims parse(String s){ return Jwts.parser().setSigningKey(password).parseClaimsJws(s).getBody(); } }
4)需要注意的是,再校验过程中,没有报错就意味着校验成功了。JWT 校验时使用的签名密钥,必须和生成 JWT 令牌时使用的密钥时配套的。如果 JWT 令牌解析校验时报错,则说明 JWT 令牌被篡改或失效了,令牌非法。