前言:在开发中,通常会在访问来到Controller层之前,我们会对请求做一些处(常见的登录功能),在处理完后,再进行决定放行或者时终止访问,同样也会在程序访问后返回到前端时作一些处理(常见的字符集统一,防止出现乱码情况)这个时候就会使用到Interceptor和Filter进行处理。
一. Interceptor
1.认识拦截器
拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。
2.拦截器的执行流程
无拦截器时
有拦截时
3.拦截器链(定义多个拦截器,就会形成一个拦截器链)
多个拦截器的执行顺序如下
熟悉了以上有拦截器项目的流程以及拦截器链中拦截器的执行顺序后,我们将定义一个拦截器。
4.自定义拦截器(基于springboot项目)
- 定义拦截器
package com.itheima.Controller.Interceptor;
import com.itheima.Utils.JwtUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**定义一个拦截器*/
@Component
public class ProjectInterceptor implements HandlerInterceptor {
/**在原始操作之前运行的代码*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//true放行
return true;
}
/**在原始操作之后运行的代码*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**在原始操作之后运行的代码,并在postHandle之后*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
需要定义一个类,并将该类实现HandlerInterceptor接口,重写里面的三个方法。三个方法的简介如下
方法名 | 方法执行顺序 |
---|---|
preHandle | 在原始操作方法之前 |
postHandle | 在原始方法操作之后 |
afterCompletion | 在postHandle方法操作之后 |
这三个方法中,return true表示放行,反之,表示拦截到,不放行,返回指定信息。
- 配置拦截器
package com.itheima.Config;
import com.itheima.Controller.Interceptor.ProjectInterceptor;
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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
/**配置方式1*/
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages").addResourceLocations("/pages");
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
这里就配置了一些拦截器要拦截那些访问路经,该类继承了WebMvcConfigurationSupport 类,进行实现的,当然,还有实现方式二如下
package com.itheima.Config;
import com.itheima.Controller.Interceptor.ProjectInterceptor;
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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
/**配置方式二,侵入式较强*/
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages").addResourceLocations("/pages");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
}
}
里面的逻辑没有任何改动,仅仅是把继承 WebMvcConfigurationSupport 类改为了实现WebMvcConfigurer接口,对比之下,第二种方式的入侵式更强一些。到这里,我们就定义了一个拦截器在项目里面了。
二.Filter
1.认识过滤器
它时javaweb的三大组件之一(Servlet程序、Listener监听器、Filter过滤器)
2.过滤器的执行流程
3.过滤器链(定义多个过滤器,就会形成一个过滤器链)
这里,我们可以基于对拦截器链的理解去理解过滤器链。以上对过滤器链认识完之后,我们将定义一个过滤器。
- 定义过滤器
package com.itheima.Controller.Filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**定义一个过滤器(javaWeb三大组件之一)*/
@WebFilter(urlPatterns = "/*")
public class ProjectFilter implements Filter {
/**初始化方法,web服务器启动,创建Filter时调用,只调用一次*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**每次拦截到请求,调用该方法,调用无数次*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("放行前的逻辑");
/**放行*/
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("放行后的逻辑");
}
/**销毁方法,web服务器关闭时调用,只调用一次*/
@Override
public void destroy() {
}
}
需要定义一个类,并将该类实现Filter接口,重写里面的三个方法。三个方法的简介如下
方法名 | 方法执行顺序 |
---|---|
init | 服务器启动时调用,只执行一次 |
doFilter | 每次拦截到请求时调用,执行若干次 |
destory | 服务器关闭时调用,只执行一次 |
在方法内调用 filterChain.doFilter(servletRequest,servletResponse)这句代码,表示放行。方形前后均可以写逻辑处理。@WebFilter(urlPatterns = "/*")表示拦截所有的请求,可根据自己的需求配,到这里过滤器还不能进行生效,它不属于spring管理,所以需要在启动类上加上如下注解。、
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
/**过滤器的配置*/
@ServletComponentScan
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
到这里,我们就成功的定义了一个过滤器。
三. Interceptor,Filter的区别。
1.拦截位置不同,如下图所示
二.归属不同
Filter属于Servlet技术,Interceptor属于SpringMVC技术 。
三. 拦截内容不同
Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强。
四.Jwt令牌实现安全验证(案例)
这里我们使用拦截器的方式,过滤器可以自己尝试。这里,只进行逻辑上的一些讲解,并不具体实现。
导入jwt坐标
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
编写一个jwt工具类
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
/**自定义一个jwt令牌的工具类*/
public class JwtUtils {
private static String signKey = "itheima";
private static Long expire = 43200000L;
/**
* 生成jwt令牌的方法*/
public static String generateJwt(Map<String ,Object> claims){
String Jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, signKey) //签名算法
.setClaims(claims) //自定义数据
.setExpiration(new Date(System.currentTimeMillis() + expire)) //指定令牌有效期
.compact();
return Jwt;
}
/**
* 解析jwt令牌的方法*/
public static Claims parseJwt(String Jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(Jwt)
.getBody();
return claims;
}
}
对jwt不了解的,可先进行对jwt做个了解。
编写一个接口,用于登录验证 (里面只有逻辑,代码可自行写)
package com.itheima.Controller;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class login {
/**登录页面
* 拿到数据进行查询,判断查询出来的数据是否为空,为空返回错误信息,不为空,生成一个jwt令牌,返回给前端,有头部的Token进行存储*/
}
编写一个拦截器,进行相关拦截
package com.itheima.Controller.Interceptor;
import com.itheima.Utils.JwtUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**定义一个拦截器*/
@Component
public class ProjectInterceptor implements HandlerInterceptor {
/**在原始操作之前运行的代码*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/**获取访问的url地址*/
String toString = request.getRequestURL().toString();
/**判断该地址是否为登录,为登录,就放行*/
if(toString.contains("login")){
return true;
}
/**获取Token*/
String token = request.getHeader("Token");
/**判断Token是否不存在,不存在返回错误信息*/
if(!StringUtils.hasLength(token)){
/**返回错误信息*/
return false;
}
try{
JwtUtils.parseJwt(token);
}catch (Exception e){
/**返回解析错误信息*/
return false;
}
//true放行
return true;
}
/**在原始操作之后运行的代码*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**在原始操作之后运行的代码,并在postHandle之后*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
到这里,我们的jwt令牌的验证就完成了,可自己根据相关提示,把该业务进行完善处理。