- Session 认证和 Token 认证
- 过滤器和拦截器
上篇文章我们讲到了过滤器和拦截器理论知识以及 SpringBoot 集成过滤器和拦截器,本篇文章我们使用过滤器和拦截器去实现基于 Token 认证的登录功能。
一、登录校验 Filter 实现
1.1、Filter 校验流程图
- 获得请求 url。
- 判断请求 url 中是否包含 login ,如果包含,说明是登录操作,放行。
- 获取请求头中的令牌(Token)
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
- 解析 token ,如果解析失败,返回错误结果(未登录)。
- 放行
1.2、Filter 校验实现
新建一个 SpringBoot 项目 loginFilter,引入之前文章提到的 JWT 工具类。
JWTUtils代码如下:
package com.duan.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
/**
* @author db
* @version 1.0
* @description JWTUtils
* @since 2023/12/31
*/
public class JWTUtils {
// 密钥
private static String signKey = "cxykk1217";
// 过期时间
private static Long expire = 1000L*60*30; // 30分钟
/**
* 生成JWT
* @param claims JWT第二部分负载payload中存储的内容
* @return
*/
public static String generateJwt(Map<String,Object> claims){
String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, signKey)
.addClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser().setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
新建 LoginFilter 类并实现 Filter ,在 doFilter 方法中进行登录校验。代码如下:
package com.duan.filter;
import com.alibaba.fastjson.JSONObject;
import com.duan.pojo.Result;
import com.duan.utils.JWTUtils;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author db
* @version 1.0
* @description LoginFilter
* @since 2024/1/11
*/
@WebFilter(urlPatterns = "/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// 1. 获得请求路径
String url = httpServletRequest.getRequestURL().toString();
// 2. 判断请求的资源路径
if(url.contains("/login")){
// 登录功能,放行
chain.doFilter(request,response);
return;
}
// 3. 获取请求头token
String token = httpServletRequest.getHeader("token");
if(token == null){
// 返回登录页面
Result noLogin = Result.error("NO_LOGIN");
// 使用原始方式给客户端响应数据
// 把noLogin 对象转成json字符串返回
String jsonString = JSONObject.toJSONString(noLogin);
httpServletResponse.getWriter().write(jsonString);
return;
}
// 4. 解析token
try{
JWTUtils.parseJWT(token);
}catch (Exception e){
// token存在问题
// 返回登录页面
Result noLogin = Result.error("NO_LOGIN");
// 使用原始方式给客户端响应数据
// 把noLogin 对象转成json字符串返回
String jsonString = JSONObject.toJSONString(noLogin);
httpServletResponse.getWriter().write(jsonString);
return;
}
// 5. token解析成功,放行
chain.doFilter(request,response);
}
}
注意:使用 @WebFilter 配置过滤器时,启动类上一定要使用 @ServletComponentScan 注解。
启动程序,通过 postman 来访问 login 和 getUser 方法。
代码地址:https://gitee.com/duan138/practice-code/tree/dev/loginFilter
二、登录校验拦截器实现
2.1、Interceptor 校验流程图
- 获得请求 url。
- 判断请求 url 中是否包含 login ,如果包含,说明是登录操作,放行。
- 获取请求头中的令牌(Token)
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
- 解析 token ,如果解析失败,返回错误结果(未登录)。
- 放行
通过流程图可以看出,过滤器和拦截器实现的流程是一样的,只不过实现方式不一样。
2.2、Interceptor 校验
新建一个 SpringBoot 项目 loginInterceptor,引入之前文章提到的 JWT 工具类。
JWTUtils 代码如下:
package com.duan.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
/**
* @author db
* @version 1.0
* @description JWTUtils
* @since 2023/12/31
*/
public class JWTUtils {
// 密钥
private static String signKey = "cxykk1217";
// 过期时间
private static Long expire = 1000L*60*30; // 30分钟
/**
* 生成JWT
* @param claims JWT第二部分负载payload中存储的内容
* @return
*/
public static String generateJwt(Map<String,Object> claims){
String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, signKey)
.addClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser().setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
新建 LoginInterceptor 类并实现 HandlerInterceptor,在 preHandle 方法中进行登录校验。代码如下:
package com.duan.handler;
import com.alibaba.fastjson.JSONObject;
import com.duan.pojo.Result;
import com.duan.utils.JWTUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author db
* @version 1.0
* @description LoginInterceptor
* @since 2023/12/20
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
// 目标方法执行前调用 true:放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
// 1. 获得请求路径
String url = request.getRequestURL().toString();
// 2. 判断请求的资源路径
if(url.contains("/login")){
// 登录功能,放行
return true;
}
// 3. 获取请求头token
String token = request.getHeader("token");
if(token == null){
// 返回登录页面
Result noLogin = Result.error("NO_LOGIN");
// 使用原始方式给客户端响应数据
// 把noLogin 对象转成json字符串返回
String jsonString = JSONObject.toJSONString(noLogin);
response.getWriter().write(jsonString);
return false;
}
// 4. 解析token
try{
JWTUtils.parseJWT(token);
}catch (Exception e){
// token存在问题
// 返回登录页面
Result noLogin = Result.error("NO_LOGIN");
// 使用原始方式给客户端响应数据
// 把noLogin 对象转成json字符串返回
String jsonString = JSONObject.toJSONString(noLogin);
response.getWriter().write(jsonString);
return false;
}
// 5. token解析成功,放行
return true;
}
// 目标方法执行后调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
// 请求处理后调用
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Completion...");
}
}
在 config 包中 LoginInterceptorConfig 方法配置新建的 loginInterceptor。
package com.duan.config;
import com.duan.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author db
* @version 1.0
* @description LoginInterceptorConfig 注册拦截器
* @since 2023/12/20
*/
@Configuration
public class LoginInterceptorConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
}
}
启动程序,通过 postman 来访问 login 和 getUser 方法。
代码地址:https://gitee.com/duan138/practice-code/tree/dev/loginInterceptor
三、总结
通过过滤器和拦截器实现基于 Token 的登录功能,加深了对过滤器和拦截器的理解,同时也梳理了基于 Token 认证登录的流程,在现在项目中常用的是基于 SpringSecurity + JWT 登录认证方式,后续我们来看一看基于 Spring security + JWT 怎么去实现登录控制。
参考
1.https://space.bilibili.com/1809189461
改变你能改变的,接受你不能改变的,关注公众号:程序员康康,一起成长,共同进步。