相关概念也就不去讨论了,网上相关文章就有很多,本文主要是想通过过滤器实现一些拦截功能并顺带验证以下两个问题:
1.过滤器能不能通过@Order或者实现Ordered接口达到多个自定义过滤器排序的目的?
2.过滤器当中到底能不能使用@Autowired注入并使用spring的资源?
需求: 我们的接口,有时是不需要token校验的,比如定时服务feign调用一些外部服务,定时服务根本没有当前登录人,也就不存在token,所以我们需要对这些服务的访问直接放行或者强制给header里面添加一个token;
定义过滤器,如果是定时服务访问,在header里面手动添加token,如果不是定时服务访问,实现token校验,如果token校验通过,需要将token里面的人员名称和编号放到参数里面;如果token校验不通过,需要让用户转到登录页面重新登录;
1.定义类FilterTestHttpServletRequestWrappe
由于我要想更改request请求的参数和header,需要定义子类继承HttpServletRequestWrapper, 并定义修改参数的方法,doFilter的request是要求ServletRequest,而HttpServletRequestWrapper是继承ServletRequestWrapper,并实现HttpServletRequest,而HttpServletRequest是继承ServletRequest,所以,我们可以自己定义一个类,继承HttpServletRequestWrapper就可以作为doFilter参数传进去;
自定义类FilterTestHttpServletRequestWrappe继承HttpServletRequestWrapper,并定义一个修改参数的方法;注意,其中获取参数值getParameterValues方法必须重写,获取请求头的getHeaders方法也必须重写,不然controller层是获取不到添加的数据的;
代码如下所示:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
public class FilterTestHttpServletRequestWrappe extends HttpServletRequestWrapper {
//参数集合
private Map<String,String[]> parameterMap;
//请求头集合
private Map<String,String> headerMap=new HashMap<>();
public FilterTestHttpServletRequestWrappe(HttpServletRequest request) {
super(request);
//构造函数将参数定义未原始请求的参数
this.parameterMap=request.getParameterMap();
}
//自定义新增参数的方法
public void addParameter(String paramName,String[] paramValues){
this.parameterMap.put(paramName,paramValues);
}
@Override
//如果用不到其实可以不用重写
public String getParameter(String name) {
if(parameterMap.containsKey(name)){
return parameterMap.get(name)[0];
}
return super.getParameter(name);
}
@Override
//这个方法是必须要重写的
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}
@Override
//这个方法是必须要重写的
public Enumeration<String> getHeaders(String name) {
List<String> headers = Collections.list(super.getHeaders(name));
if(headerMap.containsKey(name)){
headers.add(headerMap.get(name));
}
return Collections.enumeration(headers);
}
//自定义方法天加header的方法
public void addHeader(String name,String value){
headerMap.put(name,value);
}
}
2.定义第一个过滤器FilterTest1
识别是不是定时服务访问,我们定时服务的域名是http://schedule-service/:
@Component
@WebFilter(filterName = "filter1",urlPatterns = "/**")
//@Order这个注解可以实现过滤器排序
@Order(0)
public class FilterTest1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化方法!");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
//获取url
StringBuffer requestURL = request.getRequestURL();
String requestURI = request.getRequestURI();
//获取域名
String serviceDomain = requestURL.substring(0, requestURL.indexOf(requestURI));
//判断是不是定时服务
if("http://schecule-service".equals(serviceDomain)){
//手动生成一个默认token
String token=JWTUtil.generateDefaultToken();
FilterTestHttpServletRequestWrappe filterTestHttpServletRequestWrappe = new FilterTestHttpServletRequestWrappe(request);
//将token添加到header里面
filterTestHttpServletRequestWrappe.addHeader("Authorization",token);
filterChain.doFilter(filterTestHttpServletRequestWrappe,servletResponse);
}else {
//如果不是就需要判断校验token是否存在
//从header中获取token
String token = request.getHeader("Authorization");
//校验
Boolean okToken=JwtUtil.checkToken(token);
if(okToken){{
//如果校验通过就放行
filterChain.doFilter(servletRequest,servletResponse);
}else{
//如果没有token或者token校验不通过,需要重定向到登录页面
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.sendRedirect("http://localhost:3306/login.html");
}
}
@Override
public void destroy() {
}
}
3. 定义第二个过滤器FilterTest2
实现token解析,并重新封装解析后的参数:
import com.jzcs.tongYongInterface.mapper.FilterMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@WebFilter(filterName = "filter2",urlPatterns = "/**")
@Order(1)//在第一个过滤器后面执行
public class FilterTest2 implements Filter {
@Autowired
private FilterMapper filterMapper;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化方法!");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//1.校验token
HttpServletRequest request = (HttpServletRequest) servletRequest;
String token = request.getHeader("Authorization");
//解析token获取人员信息
UserInfo userInfo=JwtUtil.parseToken(token);
//通过userId查询用户的接口权限
UserAuthInfo
userAuthInfo =filterMapper.selectUserAuthInfoByUserId(userInfo.getUserId());
if(!CollectionUtils.isEmpty(userAuthInfo.getUriList())){
//判断用户有没有权限访问当前的接口
String requestURI = request.getRequestURI();
if(!userAuthInfo.getUriList().contains(requestURI)){
//如果没有,直接终止
return;
}
}
//如果校验通过,需要将token中的数据解析出来,放到参数里面
FilterTestHttpServletRequestWrappe filterTestHttpServletRequestWrappe = new FilterTestHttpServletRequestWrappe(request);
filterTestHttpServletRequestWrappe.addParameter("userName",userInfo.getUserName());
filterTestHttpServletRequestWrappe.addParameter("userId",userInfo.getUserId());
filterChain.doFilter(filterTestHttpServletRequestWrappe,servletResponse);
}
@Override
public void destroy() {
}
}
综上:是我们的过滤器的一个应用场景;回到我们的两个问题:
1.过滤器能不能通过@Order或者实现Ordered接口达到多个自定义过滤器排序的目的?
2.过滤器当中到底能不能使用@Autowired注入并使用spring的资源?
显然:我们是可以通过@Order或者实现Ordered接口达到多个自定义过滤器排序的目的;
过滤器里面也可以使用@Autowired注入并使用spring的资源,并且访问数据库的;