Spring Security介绍(三)过滤器(2)自定义

除了使用security自带的过滤器链,我们还可以自定义过滤器拦截器。

下面看下自定义的和security自带的执行顺序。

一、总结

1、自定义过滤器:

一般自定义fliter都是:

复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;

@Component
@Slf4j
public class UrlFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("进入UrlFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

但是结合spring security,这样的filter不会被执行, 需要修改为继承OncePerRequestFilter

复制代码
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@Slf4j
public class UrlFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        log.info("进入UrlFilter");
        filterChain.doFilter(request,response);
    }
}
2、执行顺序
2.1、默认顺序:

security自带的过滤器链-->自定义过滤器-->自定义拦截器。

2.2、修改自定义interceptor顺序

修改在WebMvcConfigurer中注册的顺序即可。

2.3、修改自定义filter顺序

加@Order()注解,在注解中加入数字,数字越小,优先级越高,最先执行。其中这个数字可以:

(1)自定义

复制代码
@Component
@Order(1)
public class XxxFilter extends OncePerRequestFilter{}

@Component
@Order(2)
public class Xxx1Filter extends OncePerRequestFilter{}

这时执行的顺序是: security自带的过滤器链-->自定义过滤器的顺序-->自定义拦截器的顺序。

(2)使用枚举

源码:

1)HIGHEST_PRECEDENCE

代表这个过滤器在众多过滤器中级别最高,也就是过滤的时候最先执行,这时执行的顺序为:

自定义过滤器的顺序--> security自带的过滤器链-->自定义拦截器的顺序。

2)LOWEST_PRECEDENCE

表示级别最低,最后执行过滤操作。

2.4、配置文件修改自定义filter顺序

filter的执行顺序除了可以用上面的@Order注解外,还可以通过配置文件设置,这时配置文件设置的优先级--》注解设置的优先级。如我两个filter都设置为Ordered.HIGHEST_PRECEDENCE + 1,其中一个在配置文件设置的,另外一个通过注解设置的,这时配置文件设置的那个会先执行。

复制代码
package com.demo.security.config;

import com.demo.security.filter.UrlTwoFilter;
import com.demo.security.interceptor.ParamInterceptor;
import com.demo.security.interceptor.ParamOneInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private ParamInterceptor paramInterceptor;

    @Autowired
    private ParamOneInterceptor paramOneInterceptor;


    @Autowired
    private UrlTwoFilter twoFilter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(paramOneInterceptor);
        registry.addInterceptor(paramInterceptor).addPathPatterns("/**");
        //registry.addInterceptor(paramOneInterceptor);
    }

    @Bean
    public FilterRegistrationBean<UrlTwoFilter> getIpFilter() {
        FilterRegistrationBean<UrlTwoFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(twoFilter);
        registrationBean.setEnabled(true);
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        registrationBean.setUrlPatterns(List.of("/*"));
        return registrationBean;
    }
}

@Component
@Slf4j
//@Order(1)
@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class UrlOneFilter extends OncePerRequestFilter

下面通过demo来验证下

二、demo

1、使用security过滤器
(1)编写过滤器
复制代码
package com.demo.security.filter;

import com.demo.security.constant.UserConstants;
import com.demo.security.dto.UserDTO;
import com.demo.security.util.JwtUtil;
import com.demo.security.util.RedisClient;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private RedisClient redisClient;

    public LoginFilter(AuthenticationManager authenticationManager, RedisClient redisClient) {
        //super(new AntPathRequestMatcher("/login", "POST"));
        this.authenticationManager = authenticationManager;
        this.redisClient = redisClient;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
        try {
            logger.info("进入LoginFilter");
            String userName = req.getParameter("userName");
            String passWord = req.getParameter("passWord");
            if (StringUtils.isEmpty(userName)) {
                throw new UsernameNotFoundException("请输入账号");
            }
            if (StringUtils.isEmpty(passWord)) {
                throw new UsernameNotFoundException("请输入密码");
            }
            //验证用户名密码是否正确
            Map<String, String> userMap = UserConstants.getUsers();
            if(!userMap.keySet().contains(userName)){
                throw new UsernameNotFoundException("用户不存在");
            }
            if(!passWord.equals(userMap.get(userName))){
                throw new UsernameNotFoundException("密码错误");
            }
            //这里权限返回空,由后面的授权过滤器查询
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(userName, passWord, new ArrayList<>()));
        } catch (UsernameNotFoundException e) {
            //返回错误信息
            res.setCharacterEncoding("UTF-8");
            res.setContentType("application/text;charset=utf-8");
            try {
                res.getWriter().write(e.getMessage());
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return null;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }


    @Override
    protected void successfulAuthentication(
            HttpServletRequest request,
            HttpServletResponse res,
            jakarta.servlet.FilterChain chain,
            Authentication authResult)
            throws IOException, jakarta.servlet.ServletException {
        UserDTO userDTO = (UserDTO) authResult.getPrincipal();
        String userName = userDTO.getUsername();
        String password = userDTO.getPassword();
        String jwtToken = JwtUtil.sign(userName, password);
        //缓存到redis中
        redisClient.set(userName,jwtToken);
        //返回
        res.setContentType(ContentType.TEXT_HTML.toString());
        res.getWriter().write(jwtToken);
    }
}

package com.demo.security.filter;

import com.demo.security.util.JwtUtil;
import com.demo.security.util.RedisClient;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import java.io.IOException;
@Slf4j
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
    private RedisClient redisClient;

    private UserDetailsService userDetailsService;

    public TokenAuthenticationFilter(AuthenticationManager authenticationManager, RedisClient redisClient, UserDetailsService userDetailsService) {
        super(authenticationManager);
        this.redisClient = redisClient;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        logger.info("登陆成功后访问,url={}"+req.getRequestURI());
        String token = req.getHeader("token");
        res.setCharacterEncoding("UTF-8");
        res.setContentType("application/text;charset=utf-8");
        if(StringUtils.isEmpty(token)){
            logger.info("登陆成功后访问,url={},token为空"+req.getRequestURI());
            res.getWriter().write("token为空");
            return;
        }
        //1、token是否正确,是否能反解析出userName
        String userName = JwtUtil.getUsername(token);
        if(StringUtils.isEmpty(userName)){
            logger.info("登陆成功后访问,url={},token错误"+req.getRequestURI());
            res.getWriter().write("token错误");
            return;
        }
        //2、验证token是否过期
        String redisToken = redisClient.get(userName);
        if(StringUtils.isEmpty(redisToken) || !token.equals(redisToken)){
            logger.info("登陆成功后访问,url={},token过期"+req.getRequestURI());
            res.getWriter().write("token过期");
            return;
        }
        UserDetails currentUser = userDetailsService.loadUserByUsername(userName);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(currentUser,null,currentUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(req, res);
    }
}
(2)注册到过滤器链中
复制代码
package com.demo.security.config;

//import com.demo.security.filter.AuthenticationFilter;
//import com.demo.security.filter.LoginFilter;
import com.demo.security.filter.LoginFilter;
import com.demo.security.filter.TokenAuthenticationFilter;
import com.demo.security.util.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;


import static io.netty.util.CharsetUtil.encoder;

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityWebConfig {

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private UserDetailsService userDetailsService;



    @Bean
    public PasswordEncoder passwordEncoder() {
        return new MyPasswordEncoder();
    }


    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain configure(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {

        http.csrf(AbstractHttpConfigurer::disable);
       http.headers(AbstractHttpConfigurer::disable);

       http.sessionManagement(sessionManagement -> {
           sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
       });
       http.authorizeRequests().anyRequest().authenticated().and()

               //1、登陆、退出url,均由前端拦截器控制,这里注释掉。
               //1.1、前端拦截器中判断缓存token为空,为空则post请求访问/login,目的是进入LoginFilter获取token
               //1.2、不为空则带token访问接口,如果AuthenticationFilter拦截token不合法则根据错误码跳转到登陆页面,重复1.1的操作
               //.logout().logoutUrl("/logout").and()
               //2、身份认证filter,访问系统(除了白名单接口)需要先登陆。post请求/login接口会进入这个拦截器
               // 校验用户名密码是否正确,正确返回token给前端,不正确则返回异常信息
               .addFilterBefore(new LoginFilter(authenticationManager,redisClient), LoginFilter.class)
               //3、授权filer,authenticationManager为BasicAuthenticationFilter的必传参数。所有的接口都会走到这里
               // 根据用户id查询权限,连同身份一起塞入SecurityContextHolder全局变量,后面获取用户信息则直接从SecurityContextHolder中get
               .addFilterBefore(new TokenAuthenticationFilter(authenticationManager,redisClient,userDetailsService),TokenAuthenticationFilter.class);
       return http.build();
    }

}
2、自定义过滤器拦截器
2.1、过滤器
复制代码
package com.demo.security.filter;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@Slf4j
public class UrlFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        log.info("进入UrlFilter");
        filterChain.doFilter(request,response);
    }
}

package com.demo.security.filter;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@Slf4j
public class UrlOneFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        log.info("进入UrlFilter-one");
        filterChain.doFilter(request,response);
    }
}
2.2、拦截器

(1)编写拦截器

复制代码
package com.demo.security.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
@Slf4j
public class ParamInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入ParamInterceptor");
        return true;
    }
}

package com.demo.security.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
@Slf4j
public class ParamOneInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("进入ParamInterceptor-one");
        return true;
    }
}

(2)注册

复制代码
package com.demo.security.config;

import com.demo.security.interceptor.ParamInterceptor;
import com.demo.security.interceptor.ParamOneInterceptor;
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;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private ParamInterceptor paramInterceptor;

    @Autowired
    private ParamOneInterceptor paramOneInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(paramInterceptor).addPathPatterns("/**");
        registry.addInterceptor(paramOneInterceptor);
    }
}
3、测试默认顺序

(1)LoginFilter为特定情况下才能进入(/login的post请求),认证成功即返回:

访问localhost:2222/securityDemo/login?userName=zs&passWord=123后台打印

复制代码
2024-04-27T09:14:57.210+08:00  INFO 14836 --- [nio-2222-exec-3] com.demo.security.filter.LoginFilter     : 进入LoginFilter

(2)访问其他接口,如localhost:2222/securityDemo/menu/test

如果沒有登录,在TokenAuthenticationFilter就返回了,控制台打印

复制代码
2024-04-27T09:31:01.715+08:00  INFO 37920 --- [nio-2222-exec-3] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={}/securityDemo/menu/test
2024-04-27T09:31:01.734+08:00  INFO 37920 --- [nio-2222-exec-3] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={},token过期/securityDemo/menu/test

如果登录了,后台打印

复制代码
2024-04-27T09:32:56.046+08:00  INFO 37920 --- [nio-2222-exec-7] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={}/securityDemo/menu/test
2024-04-27T09:32:56.064+08:00  INFO 37920 --- [nio-2222-exec-7] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-27T09:32:56.064+08:00  INFO 37920 --- [nio-2222-exec-7] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-27T09:32:56.070+08:00  INFO 37920 --- [nio-2222-exec-7] c.d.s.interceptor.ParamInterceptor       : 进入ParamInterceptor
2024-04-27T09:32:56.070+08:00  INFO 37920 --- [nio-2222-exec-7] c.d.s.interceptor.ParamOneInterceptor    : 进入ParamInterceptor-one

可以看到,执行顺序是security自带的过滤器链-->自定义过滤器-->自定义拦截器

4、修改执行顺序

下面修改自定义的过滤器拦截器的顺序:

4.1、拦截器

在WebMvcConfigurer中修改注册顺序

复制代码
package com.demo.security.config;

import com.demo.security.interceptor.ParamInterceptor;
import com.demo.security.interceptor.ParamOneInterceptor;
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;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private ParamInterceptor paramInterceptor;

    @Autowired
    private ParamOneInterceptor paramOneInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(paramOneInterceptor);
        registry.addInterceptor(paramInterceptor).addPathPatterns("/**");
        //registry.addInterceptor(paramOneInterceptor);
    }
}
4.2、过滤器
复制代码
package com.demo.security.filter;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@Slf4j
@Order(2)
public class UrlFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        log.info("进入UrlFilter");
        filterChain.doFilter(request,response);
    }
}

package com.demo.security.filter;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@Slf4j
@Order(1)
public class UrlOneFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        log.info("进入UrlFilter-one");
        filterChain.doFilter(request,response);
    }
}

再次测试,可以看到执行顺序已经修改了

复制代码
2024-04-27T09:39:09.419+08:00  INFO 65540 --- [nio-2222-exec-3] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={}/securityDemo/menu/test
2024-04-27T09:39:09.471+08:00  INFO 65540 --- [nio-2222-exec-3] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-27T09:39:09.471+08:00  INFO 65540 --- [nio-2222-exec-3] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-27T09:39:09.476+08:00  INFO 65540 --- [nio-2222-exec-3] c.d.s.interceptor.ParamOneInterceptor    : 进入ParamInterceptor-one
2024-04-27T09:39:09.476+08:00  INFO 65540 --- [nio-2222-exec-3] c.d.s.interceptor.ParamInterceptor       : 进入ParamInterceptor

执行顺序为:security自带的过滤器链-->自定义过滤器的顺序-->自定义拦截器的顺序。

5、设置自定义filter优先级最高

(1)只修改一个

复制代码
package com.demo.security.filter;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@Slf4j
//所有filter中优先级最高
@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class UrlFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        log.info("进入UrlFilter");
        filterChain.doFilter(request,response);
    }
}

另外一个不变:

复制代码
@Component
@Slf4j
@Order(1)
public class UrlOneFilter extends OncePerRequestFilter

这时访问登录接口后台打印:

复制代码
2024-04-28T11:11:31.846+08:00  INFO 37052 --- [nio-2222-exec-5] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-28T11:11:31.847+08:00  INFO 37052 --- [nio-2222-exec-5] com.demo.security.filter.LoginFilter     : 进入LoginFilter

再访问localhost:2222/securityDemo/menu/test,后台打印:

复制代码
2024-04-28T11:10:53.685+08:00  INFO 37052 --- [nio-2222-exec-4] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-28T11:10:53.686+08:00  INFO 37052 --- [nio-2222-exec-4] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={}/securityDemo/menu/test
2024-04-28T11:10:53.688+08:00  INFO 37052 --- [nio-2222-exec-4] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-28T11:10:53.689+08:00  INFO 37052 --- [nio-2222-exec-4] c.d.s.interceptor.ParamOneInterceptor    : 进入ParamInterceptor-one
2024-04-28T11:10:53.689+08:00  INFO 37052 --- [nio-2222-exec-4] c.d.s.interceptor.ParamInterceptor       : 进入ParamInterceptor

可以看到,自定义的filter比security自带的过滤器链顺序都高。

(2)再把另外一个filter优先级也提高:

复制代码
@Component
@Slf4j
//@Order(1)
@Order(Ordered.HIGHEST_PRECEDENCE+2)
public class UrlOneFilter extends OncePerRequestFilter

访问登录接口localhost:2222/securityDemo/login?userName=zs&passWord=123

复制代码
2024-04-28T11:14:35.904+08:00  INFO 56456 --- [nio-2222-exec-2] o.a.c.c.C.[.[localhost].[/securityDemo]  : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-04-28T11:14:35.904+08:00  INFO 56456 --- [nio-2222-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-04-28T11:14:35.904+08:00  INFO 56456 --- [nio-2222-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
2024-04-28T11:14:35.904+08:00  INFO 56456 --- [nio-2222-exec-2] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-28T11:14:35.904+08:00  INFO 56456 --- [nio-2222-exec-2] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-28T11:14:35.919+08:00  INFO 56456 --- [nio-2222-exec-2] com.demo.security.filter.LoginFilter     : 进入LoginFilter

再访问localhost:2222/securityDemo/menu/test,后台打印:

复制代码
2024-04-28T11:15:30.019+08:00  INFO 56456 --- [nio-2222-exec-5] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-28T11:15:30.019+08:00  INFO 56456 --- [nio-2222-exec-5] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-28T11:15:30.020+08:00  INFO 56456 --- [nio-2222-exec-5] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={}/securityDemo/menu/test
2024-04-28T11:15:30.021+08:00  INFO 56456 --- [nio-2222-exec-5] c.d.s.interceptor.ParamOneInterceptor    : 进入ParamInterceptor-one
2024-04-28T11:15:30.021+08:00  INFO 56456 --- [nio-2222-exec-5] c.d.s.interceptor.ParamInterceptor       : 进入ParamInterceptor
6、测试相同Order顺序

再加一个filter,且优先级都设置成相同的:

复制代码
@Component
@Slf4j
//所有filter中优先级最高
@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class UrlFilter extends OncePerRequestFilter

@Component
@Slf4j
//@Order(1)
@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class UrlOneFilter extends OncePerRequestFilter


@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class UrlTwoFilter extends OncePerRequestFilter

测试,执行顺序为:

复制代码
2024-04-28T11:28:38.217+08:00  INFO 41668 --- [nio-2222-exec-5] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-28T11:28:38.217+08:00  INFO 41668 --- [nio-2222-exec-5] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-28T11:28:38.218+08:00  INFO 41668 --- [nio-2222-exec-5] com.demo.security.filter.UrlTwoFilter    : 进入UrlFilter-two

现在将two放到配置文件中指定顺序:

复制代码
package com.demo.security.config;

import com.demo.security.filter.UrlTwoFilter;
import com.demo.security.interceptor.ParamInterceptor;
import com.demo.security.interceptor.ParamOneInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private ParamInterceptor paramInterceptor;

    @Autowired
    private ParamOneInterceptor paramOneInterceptor;


    @Autowired
    private UrlTwoFilter twoFilter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(paramOneInterceptor);
        registry.addInterceptor(paramInterceptor).addPathPatterns("/**");
        //registry.addInterceptor(paramOneInterceptor);
    }

    @Bean
    public FilterRegistrationBean<UrlTwoFilter> getIpFilter() {
        FilterRegistrationBean<UrlTwoFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(twoFilter);
        registrationBean.setEnabled(true);
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        registrationBean.setUrlPatterns(List.of("/*"));
        return registrationBean;
    }
}

再次查看执行顺序:

复制代码
2024-04-28T11:31:22.754+08:00  INFO 18328 --- [nio-2222-exec-3] com.demo.security.filter.UrlTwoFilter    : 进入UrlFilter-two
2024-04-28T11:31:22.754+08:00  INFO 18328 --- [nio-2222-exec-3] com.demo.security.filter.UrlFilter       : 进入UrlFilter
2024-04-28T11:31:22.754+08:00  INFO 18328 --- [nio-2222-exec-3] com.demo.security.filter.UrlOneFilter    : 进入UrlFilter-one
2024-04-28T11:31:22.754+08:00  INFO 18328 --- [nio-2222-exec-3] c.d.s.filter.TokenAuthenticationFilter   : 登陆成功后访问,url={}/securityDemo/menu/test
2024-04-28T11:31:22.754+08:00  INFO 18328 --- [nio-2222-exec-3] c.d.s.interceptor.ParamOneInterceptor    : 进入ParamInterceptor-one
2024-04-28T11:31:22.754+08:00  INFO 18328 --- [nio-2222-exec-3] c.d.s.interceptor.ParamInterceptor       : 进入ParamInterceptor
相关推荐
LI JS@你猜啊8 分钟前
window安装docker
java·spring cloud·eureka
书中自有妍如玉18 分钟前
.net 使用MQTT订阅消息
java·前端·.net
风铃儿~44 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组1 小时前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿1 小时前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA1 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng0061 小时前
Redis看门狗机制
java·数据库·redis
我是唐青枫1 小时前
.NET AOT 详解
java·服务器·.net
Su米苏2 小时前
Axios请求超时重发机制
java
Undoom2 小时前
🔥支付宝百宝箱新体验!途韵归旅小帮手,让高铁归途变旅行
后端