JWT
全称:JSON Web Token:定义了一种简洁的(就是个字符串)、自包含(可以存信息)的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的,不会被篡改的。
由三个部分组成,点分字符串形式,1,2由Base64编码得到,3由1中指定的算法算出来:
- Header(头):记录令牌类型、签名算法等。例如:{"algg":"HS256", type": "JWT"}
- Payload(有效载荷):携带一些自定义信息、默认信息等。例如:{"id":"1","username":"Tom"}
- Signature(签名):防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
生成JWT
- 引入依赖
io.jsonwebtoken
jjwt
0.9.1
- 使用提供的工具类JWTs生成
String jwt=Jwts.builder()
.signWith(SignatureAlgorithm.H5256,"密钥")//签名算法会
.setClaims(claims)//自定义内容(载荷)
.setExpiration(new Date(System.currentTimeMillis() + 12*3600*10000)//有效期,注意Date中构造参数为毫秒值
.compact();
解析JWT
Claims claims = Jwts.parser()
.setSigningKey("密钥")
.parseClaimsJws("JWT字符串")
.getBody();// 拿到第二部分负载
场景:登录认证
- 登录成功后,生成令牌并返回给前端
- 后续每个请求,都要携带JWT令牌
- 系统在每次请求处理之前,先校验令牌,通过后,再处理
常用Filter或者Interceptor进行统一处理
统一检验token-Filter
Filter是javaWeb三大组件(Servlet、Filter、Listener)之一
Filter准备工作
- 在启动类上添加@ServletComponentScan注解
- 创建一个Filter类实现javax.servlet.Filter接口
- Filter类上加@WebFilter(urlPatterns = "),注解,配置拦截资源的路径。
- 有三个方法可以实现,可以只实现doFilter:
init(FilterConfig filterConfig):初始化方法,只会调用一次doFilter(ServletRequest request, ServletResponse response,FilterChain chain)destory:销毁方法,服务器销毁时调用,只调用一次
Filter拦截路径
通过@WebFilter(urlPatterns = "xxx")配置拦截路径,有 3 种常见方式:
| 拦截类型 | 拦截路径 | 拦截规则 |
|---|---|---|
| 具体路径拦截 | /login | 仅访问/login时拦截 |
| 目录拦截 | /books/* | 访问/books下所有资源时拦截 |
| 全部拦截 | /* | 访问所有资源时拦截 |
Filter执行流程
- 请求到达 Filter:浏览器请求先被 Filter 拦截
- 放行前逻辑:执行
chain.doFilter()之前的代码(如日志、权限校验) - 放行到 web 资源:通过
chain.doFilter()将请求传递给接口 - 资源处理并返回:web 资源处理请求后,响应回到 Filter
- 放行后逻辑:执行
chain.doFilter()之后的代码(如统一响应处理)
注意事项:放行和return不同,他是会回来执行下面的代码的,所以不想让让他执行下面的就多加一个return
过滤器链
- 介绍:一个web应用中,可以配置多个过滤器,这多个过过滤器就形成了一个过滤器链。
- 顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。
登录校验Filter-登录校验流程
- 在doFilter(ServletRequest request, ServletResponse response,FilterChain chain)方法中
- 需要将参数强转为HttpServletRequest以及HttpServletResponse
- 判断请求若是登录操作,不拦截直接放行
- 获取请求头中的令牌(token)
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)
- 解析token,如果解析失败,返回错误结果(未登录)
- 放行
注意:其中返回错误结果提示前端要自己写,如下
Result error = Result.error("NOT_LOGIN");
//手动转换对象->json
String notLogin = JSONObject.toJSONString(error)
resp.getWriter().write(notLogin);
统一检验token-Interceptor
Spring相架中提供的,用来动态拦截控制器方法的执行,类似于过滤器。
作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
Interceptor准备工作
- 创建一个Interceptor类实现HandlerInterceptor接口
- 有三个方法可以实现,这三个方法都有默认实现:
boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler):controller前执行,返回值true放行,false不放行postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, MModelAndView modelAndView):controller后执行afterCompletion (HttpServletRequesit req, HttpServletResponse resp, Object handller, Exception ex):视图渲染完毕后执行,最后执行
- 注册拦截器:
首先用@Component注解将拦截器给IOC容器管理,创建一个配置类并加上@Configuration,实现WebMvcConfigurer接口,示例代码如下
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginCheckInterceptor loginCheckInteerceptor @Override public void addInterceptors((InterceptorRegistry registry) { registry .addInterceptor(loginCheckInterceptor) .addPathPatterns("/**") //需要拦截哪些资源 .excludePathPatterns("/login"); //不需要拦截哪些资源; } }
Interceptor拦截路径
| 拦截路径 | 含义 | 匹配示例 |
|---|---|---|
/* | 一级路径 | 能配/depts,不能配/depts/1 |
/** | 任意级路径 | 能配/depts,/depts/1,/depts/1/2等 |
/depts/* | /depts下一级路径 | 能配/depts/1,不能配/depts/1/2 |
/depts/** | /depts下任意级路径 | 能配/depts,/depts/1,/depts/1/2 |
Interceptor执行流程
-
请求阶段:
- 浏览器发请求 → 进入 Filter
- Filter 执行
doFilter()放行前逻辑 → 放行到 DispatcherServlet - DispatcherServlet 转发 → Interceptor 执行
preHandle()→ 进入 Controller 执行业务
-
响应阶段:
- Controller 处理完成 → Interceptor 执行
postHandle() - 请求完全完成 → Interceptor 执行
afterCompletion()→ 回到 DispatcherServlet - 回到 Filter → 执行
doFilter()放行后逻辑 → 响应返回浏览器
- Controller 处理完成 → Interceptor 执行
Filter 与 Interceptor的不同点
- 接口规范不同:
- 过滤器需要实现Filter接口
- 拦截器需要实现Handlerintercepto接口
- 拦截范围不同:
- 过滤器Filter会拦截所有的资源
- Interceptor只会拦截Spring环境中的资源
登录校验Interceptor-登录校验流程
- boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler)中
- 获取请求头中的令牌(token)
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)并
return false; - 解析token,如果解析失败,返回错误结果(未登录)并
return false; return true;