- 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 {@Overridepublic 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. 获取请求头tokenString 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. 解析tokentry{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:放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {// 1. 获得请求路径String url = request.getRequestURL().toString();// 2. 判断请求的资源路径if(url.contains("/login")){// 登录功能,放行return true;}// 3. 获取请求头tokenString 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. 解析tokentry{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;}// 目标方法执行后调用@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...");}// 请求处理后调用@Overridepublic 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 {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic 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
改变你能改变的,接受你不能改变的,关注公众号:程序员康康,一起成长,共同进步。