1、会话技术
- 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术
各跟踪方案优缺点:
Cookie:
优点:
- 简单易用:Cookie 是浏览器内置的机制,使用起来相对简单。
- 前端控制:可以在前端轻松设置和读取,无需服务器参与,使得某些场景下的状态跟踪更方便。
缺点:
- 安全性:Cookie 存储在客户端,可能会被篡改或盗取,因此不适合存储敏感信息。
- 大小限制:每个域名下的 Cookie 数量和总大小都有限制,可能会导致存储不足问题。
- 跨域问题:Cookie 通常只能在设置它的域名下使用,不方便在不同域名之间共享会话信息。
Session:
优点:
- 安全性:Session 数据存储在服务器上,相对于 Cookie 更加安全,可以存储敏感信息。
- 灵活性:可以存储复杂的数据结构,适合存储会话相关的数据。
- 跨域问题:Session 数据存储在服务器上,因此可以在不同域名之间共享。
缺点:
- 存储开销:每个会话都需要在服务器上存储相关数据,可能增加服务器的负担。
- 扩展性:需要使用外部存储或者分布式方案,以支持高负载或多服务器环境。
令牌技术(如JWT):
优点:
- 无状态:令牌技术基于无状态的原则,服务器无需在后端存储会话数据,适用于分布式架构。
- 跨平台:令牌可以在不同的平台和服务之间传递,适用于前后端分离的应用。
- 可扩展性:可以在令牌中包含自定义的信息,适应各种场景的需求。
缺点:
- 安全性:令牌被篡改可能会导致安全问题,需要采取安全措施如加密和签名。
- 令牌大小:令牌可能会比传统的会话标识(如Session ID)更大,增加网络传输开销。
- 处理复杂度:在服务器端需要验证令牌的合法性,可能需要一些额外的代码和操作。
2、JWT技术
1)全称:JSON Web Token
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
2)组成:
- 第一部分: Header(头),记录令牌类型、签名算法等。例如: {"alg":"HS256","type":"JWT")
- 第二部分: Payload(有效载荷),携带一些自定义信息、默认信息等。例如: ("id":"1" "username":"Tom")
- 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
3)场景:登录认证
- 登录成功后,生成令牌
- 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理
4)JWT生成
示例:
@Test
public void testGenJwt(){
Map<String, Object> clamis = new HashMap<>();
clamis.put("age",18);
clamis.put("username","Young");
String jwt= Jwts.builder()
.setClaims(clamis)//自定义内容(载荷)
.signWith(SignatureAlgorithm.HS256,"Young")//签名算法
.setExpiration(new Date(System.currentTimeMillis() + 3600*1000))//有效期设置为一小时
.compact();
System.out.println(jwt);
}
以上代码中,Jwts.builder()
:这是创建 JWT 令牌的生成器的起始点。可以通过它来逐步配置生成 JWT 所需的各个部分。
然后声明一个clamis对象,这里的 claims
是一个 Map<String, Object>
,它包含了我希望在 JWT 的载荷(Payload)中添加的声明(claims)。在 JWT 中,载荷是一组包含有关令牌的信息的键值对。然后我将信息放入clamis中。
而其中的.signWith(SignatureAlgorithm.HS256, "Young")
:这是指定如何对 JWT 进行签名的部分。SignatureAlgorithm.HS256
表示使用 HMAC SHA-256 签名算法,Young
则是用于签名的密钥。
.setExpiration(new Date(System.currentTimeMillis() + expire))
:这里设置了 JWT 的过期时间。这里设置为1小时(毫秒数),用于指定从当前时间开始,多长时间后 JWT 将过期。
.compact()
:这一步将各个配置组合起来生成最终的 JWT 令牌。
5)JWT解析
示例:
@Test
public void testParseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("Young")//此处放入你所设置的签名密钥
.parseClaimsJws("You JWT")//此处放入生成的jwt密钥
.getBody();
System.out.println(claims);
}
附加:jwt生成和解码工具类:
public class JwtUtils {
private static String signKey = "Young";
private static Long expire = 43200000L;//设置有效期为24小时
/**
* 生成JWT令牌
* @param claims JWT第二部分负载 payload 中存储的内容
* @return
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
注意要添加依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>