目录
给一个场景:实现登录
传统思路:
• 登陆页面把用户名密码提交给服务器.
• 服务器端验证用户名密码是否正确, 并返回校验结果给后端
• 如果密码正确, 则在服务器端创建 Session . 通过 Cookie 把 sessionId 返回给浏览器.
问题: 集群环境下无法直接使用Session。
1.session存在服务器中,服务器重启,session丢失,用户需要重新登录。
2.用户第一次请求,分配在服务器1,session存在服务器1上。用户第二次请求,分配在服务器2,服务器2没有用户的session,校验失败,认为用户没有登录,让用户重新登录。
解决方案:
1.session存储在一个公用机器(或者缓存中)
2.token(常用)
token:带有一定信息的字符串,token是客户端进行访问时携带的;token不能伪造;token不是用来加密,是用来防止篡改。
令牌技术
令牌其实就是⼀个用户身份的标识
使用令牌技术解释场景:
- 用户登录 用户登录请求, 经过负载均衡, 把请求转给了第一台服务器, 第一台服务器进行账号密码验证, 验证成功后, 生成⼀个令牌, 并返回给客户端。
- 客户端收到令牌之后, 把令牌存储起来. 可以存储在Cookie中, 也可以存储在其他的存储空间(比如 localStorage)
- 查询操作 用户登录成功之后, 携带令牌继续执行查询操作, 比如查询博客列表. 此时请求转发到了 第二台机器, 第二台机器会先进行权限验证操作.。服务器验证令牌是否有效, 如果有效, 就说明用户已经执行了登录操作, 如果令牌是无效的, 就说明用户之前未执行登录操作。
令牌的优缺点
优点: 1.解决了集群环境下的认证问题
2.减轻服务器的存储压力(无需在服务器端存储)
缺点: 1.需要自己实现(包括令牌的生成, 令牌的传递, 令牌的校验)
当前企业开发中, 解决会话跟踪使用最多的方案就是令牌技术
实现令牌的方式
JWT令牌
JWT全称: JSON Web Token
JSON Web Token(JWT) 用于客户端和服务器之间传递安全可靠的信息。其本质是一个token, 是一种紧凑的URL安全方法。
JWT令牌的生成和校验
1.引入JWT令牌的依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred-->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
2.生成令牌
public class JwtUtilTest {
//过期时间,30分钟
private static final long expiration=30*60*1000;
//1.生成token
//2.验证token
@Test
public void getToken(){
Key key= Keys.hmacShaKeyFor(Decoders.BASE64.decode("asddfghhjjzzzzzzaqwexfgff"));//签名的编码方式
Map<String,Object> claim=new HashMap<>();
claim.put("id",5);
claim.put("name","zhangsan");
//setClaims()自定义载荷(要存储的信息)
String token=Jwts.builder()
.setClaims(claim)//生成载荷
.setExpiration(new Date(System.currentTimeMillis()+expiration))//生成过期时间
.signWith(key)//生成签名
.compact();//生成token
System.out.println(token);
}
}
运行:
加长字符串长度,得到token,将token解析:
自动随机生成key
@Test
public void genKey(){
//随机生成一个key
SecretKey secretKey= Keys.secretKeyFor(SignatureAlgorithm.HS256);
String key = Encoders.BASE64.encode(secretKey.getEncoded());
System.out.println(key);
}
把这个生成的key带入上面,位数不会出现错误,即可生成token。
3.校验令牌
完成了令牌的生成, 我们需要根据令牌, 来校验令牌的合法性(以防客户端伪造)
//校验令牌
@Test
public void parseToken(){
String token ="eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6NSwiZXhwIjoxNzA5MTk2MjE2fQ.8Kh05VSkgmXl2A_UtCU0GWlIVlNDApzpQ6wSMOoCsQ4";
JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
Claims body = build.parseClaimsJws(token).getBody();
System.out.println(body);
}