目录
需要解决的问题:用户在未登录状态下,也可以通过访问网址直接跳过登录界面
实现思路
通过登录成功的标记来检测,在每个接口前做一个标记判断是否登录,若没登录则返回错误信息,并使前端退出
但这样较为繁琐,因此我们可以通过一种统一拦截的技术来拦截所有请求.这涉及到了会话跟踪这一技术.
会话跟踪的三个方案--引出Jwt令牌技术
1.访问cookie的值,在同一会话的不同请求之间共享数据
跨域:只要协议/IP地址/端口三个维度任何一个不同,就是跨域操作
2.session
3.现代普遍采用的令牌技术--JWT令牌
JWT令牌技术
一个令牌包含三个部分:头/有效载荷/签名:防止令牌被篡改
核心的两个步骤:
生成令牌 & 校验令牌
第一步--生成令牌
1.引入依赖
2.生成令牌
1.签名算法
2.自定义内容
3.令牌有效期
以下为测试样例:
@Test
public void JwtTest()
{
Map<String, Object> claims = new HashMap<>();
claims.put("id", 1);
claims.put("name", "tom");
/*此处负载的内容是id和姓名*/
String jwt = Jwts.builder()
/*设置签名算法*/
.signWith(SignatureAlgorithm.HS256, "theresa")
/*自定义内容(负载)*/
.setClaims(claims)
/*设置有效期为一小时*/
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
.compact();
}
第二步--校验令牌
报错的两种情况:过期/被篡改
.setSigningKey("")填入和开始输入的一样,签名算法
.parseClaimsJws("")中填入要校验的令牌
public void ParseJwt(String jwt){
//许可验证
Claims claims = Jwts.parser()
.setSigningKey(signKey)//传递签名
.parseClaimsJws(jwt)//传递令牌
.getBody();//得到内容
System.out.println(claims);
}
第三步--登录下发令牌
@PostMapping("/login")
public Result login(@RequestBody Emp emp){
log.info("员工登录:{}",emp);
Emp e = empService.login(emp);
//若登录成功,生成令牌并下发令牌
if(e != null)
{
//定义claims携带当前登录的员工信息
Map<String,Object> claims = new HashMap<>();
claims.put("id",e.getId());
claims.put("name",e.getName());
claims.put("username",e.getUsername());
//此处将claims传入了我们第一步定义的生成令牌工具类中
String jwt = JwtUtils.generateJwt(claims);
return Result.success(jwt);
}
//若登录失败,返回错误信息
return Result.error("用户名或密码错误");
}
}
ThreadLocal不是一个Thread,而是Thread的局部变量
ThreadLocal为每个线程单独提供一个存储空间,具有线程隔离效果,只有在线程内才能获取到对应的值,线程外则不能访问。
客户端每一次访问,tomacat都会开辟一个线程
所以当我们保证在线程的生命周期只内,能访问到,就可以把值放到里面。
解决原理
代码中已经提供了封装好的ThreadLocal,名字是BaseContext, 因为每次请求都会用开辟一个线程,而且每次请求前端都会带token过来,所以它发送保存用户的时候也会带token过来进行验证.
所以将jwt验证出来的token放置在ThreadLocal中
因为这次保存和他是一个线程,所以ThreadLocal还是一个,所以保存的时候直接从ThreadLocal中获取即可
解决方式
在jwt验证的代码中放置token到ThreadLocal
在service中获取ThreadLocal将id放进去验证