1 SpringMVC中的拦截器
1.1 拦截器的作用
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
用户可以自己定义一些拦截器来实现特定的功能。
谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但是也有区别,接下来我们就来说说他们的区别:
过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
过滤器在 url-pattern 中配置了/之后,可以对所有要访问的资源拦截。
拦截器它是只会拦截访问的控制器(controller)方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
它也是 AOP 思想的具体应用。
我们要想自定义拦截器, 要求必须实现:HandlerInterceptor接口,或者继承Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器
过滤器能做拦截器所有能做的事,但是过滤器能做的事,拦截器可能就处理不了
*
2 自定义拦截器
2.1 编写一个类实现HandlerInterceptor接口
重写接口中的方法,HandlerInterceptor接口中给出了默认的方法实现,如果我们不想写自己的实现,可以使用默认的实现,我们也可以编写自己的实现,
接口中提供了三个方法
(1) preHandle:这个是用来预处理的,会在controller控制器中的方法执行前执行,
(2) postHandle:这个方法是用来后置处理的,会在controller控制器中的方法执行完后执行
(3) afterCompletion:这个方法用来做最终的处理,就是controller控制器,jsp等页面都执行完了再执行
IDEA重写方法小技巧:教程中因为没改IDEA的输入配置,用的就是IDEA的默认输入方式,包括快捷键啥的,而我改为了eclipse模式,所以教程中的有些快捷键并不好使(比如重写方法IDEA快捷键CTRL + O),但是别忘记了强大的alt + insert,这个快捷键,就是为快速生成提供的,可以提供很多快速生成
public class CustomerInterceptor implements HandlerInterceptor {private static final Logger logger = LoggerFactory.getLogger(CustomerInterceptor.class);/***预处理,controller方法执行前*return true,放行,执行下一个拦截器,如果没有执行controller中的方法* return false,不放行,*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (logger.isInfoEnabled()) {logger.info("自定义拦截器中的预处理方法执行了,handler:{}",handler.toString());}return true;}}
2.2配置拦截器
<!--配置springMVC拦截器--><mvc:interceptors><!--配置具体的哪一个拦截器--><mvc:interceptor><!--配置要拦截的具体方法--><mvc:mapping path="/interceptor/*"/><!--配置拦截器不拦截的方法,一般mapping,exclude-mapping配置一个就好<mvc:exclude-mapping path=""/>--><!--配置编写的拦截器--><bean class="com.bozhao.interceptor.CustomerInterceptor"/></mvc:interceptor></mvc:interceptors>
2.3运行结果
3 使用拦截器方式实现token鉴权
如果我们每个方法都去写一段代码,冗余度太高,不利于维护,那如何做使我们的代码看起来更清爽呢?我们可以将这段代码放入拦截器去实现,这也是AOP思想的实现
3.1 添加拦截器
Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)在preHandle中,可以进行编码、安全控制等处理;在postHandle中,有机会修改ModelAndView;在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
(1)创建拦截器类。
package com.tensquare.user.interceptor;import io.jsonwebtoken.Claims;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import util.JwtUtil;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/*** @author: Luck-zb* description:Jwt拦截器* Date:2021/1/31 - 12:57*/@Component // 交给spring容器,相当于xml配置方式配置拦截器public class JwtInterceptor implements HandlerInterceptor {private static Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);@Autowiredprivate JwtUtil jwtUtil;public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 获取到token的请求头String header = request.getHeader("Authorization");if (StringUtils.isNotBlank(header)) {if (header.startsWith("Bearer ")) {// 得到当前的令牌tokenString token = header.substring(7);if (logger.isInfoEnabled()) {logger.info("当前令牌token: {}", token);}// 解析tokentry {Claims claims = jwtUtil.parseJWT(token);// 得到当前用户的角色String roles = (String) claims.get("roles");if (StringUtils.isNotBlank(roles)) {// 把不同角色的用户解析后的token request域对象中if("user".equals(roles)) {request.setAttribute("user_claims", claims);}if ("admin".equals(roles)) {request.setAttribute("admin_claims", claims);}}} catch (Exception e) {e.printStackTrace();}}}return true;}}
(2)配置拦截器类
package com.tensquare.user.config;import com.tensquare.user.interceptor.JwtInterceptor;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.WebMvcConfigurationSupport;/*** @author: Luck-zb* description:springMVC配置类,WebMvcConfigurationSupport表示所有springMVC的配置文件,都可以选择此类 * 进行配置* Date:2021/1/31 - 12:59*/@Configurationpublic class SpringMvcConfig extends WebMvcConfigurationSupport{@Autowiredprivate JwtInterceptor jwtInterceptor;/*** Override this method to add Spring MVC interceptors for* pre- and post-processing of controller invocation.* @see InterceptorRegistry*/protected void addInterceptors(InterceptorRegistry registry) {// 指定拦截器对象和该拦截器要拦截的路径registry.addInterceptor(jwtInterceptor).addPathPatterns("/**").excludePathPatterns("/**/login/**"); // 根据请求url放行登录请求}}
3.2 拦截器验证token
重写preHandle()方法
package com.tensquare.user.interceptor;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import util.JwtUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author: Luck-zb
* description:Jwt拦截器
* Date:2021/1/31 - 12:57
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
private static Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);
@Autowired
private JwtUtil jwtUtil;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 获取到token的请求头
String header = request.getHeader("Authorization");
if (StringUtils.isNotBlank(header)) {
if (header.startsWith("Bearer ")) {
// 得到当前的令牌token
String token = header.substring(7);
if (logger.isInfoEnabled()) {
logger.info("当前令牌token: {}", token);
}
// 解析token
try {
Claims claims = jwtUtil.parseJWT(token);
// 得到当前用户的角色
String roles = (String) claims.get("roles");
if (StringUtils.isNotBlank(roles)) {
// 把不同角色的用户解析后的token request域对象中
if("user".equals(roles)) {
request.setAttribute("user_claims", claims);
}
if ("admin".equals(roles)) {
request.setAttribute("admin_claims", claims);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
}
