SpringSecurity6解决requestMatchers().permitAll()后依然执行自定义过滤器的问题

第一个原因

Spring容器会自动识别被注册成为Bean对象的Filter过滤器(即继承自Filter对象的类),将这个Bean对象自动注册到SpringBoot的过滤器链中。

如果你的过滤器类使用注解注册成为了一个Bean对象,那么他就已经加到了SpringBoot的过滤器链中,所以就算你的SpringSecurity配置中设置了permitAll,可能还会去走SpringBoot的过滤器链。

第一个原因解决

不要将自己要加入到Security过滤器链的过滤器注册成为Bean对象,而是想办法传值给Security的配置类配置过滤器

例如我没有给自定义过滤器加注解注册成为Bean对象,但是在Security类中new自己的自定义过滤器,将他给Security配置过滤器

/**
 * @Author Yan
 * @Date 2023/06/04 8:37
 * @Version 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private UserMapper userMapper;

    public JwtAuthenticationFilter authenticationJwtTokenFilter() {
        return new JwtAuthenticationFilter(userMapper,userDetailsService);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        // 提供自定义loadUserByUsername
        authProvider.setUserDetailsService(userDetailsService);
        // 指定密码编辑器
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 禁用basic明文验证
                .httpBasic().disable()
                // 禁用默认登录页
                .formLogin().disable()
                // 禁用默认登出页
                .logout().disable()
                // 设置异常的EntryPoint,如果不设置,默认使用Http403ForbiddenEntryPoint
//                .exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(invalidAuthenticationEntryPoint))
                // 前后端分离是无状态的,不需要session了,直接禁用。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests((authorizeRequests -> {
                    authorizeRequests
                            // 允许所有OPTIONS请求
                            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                            // 允许直接访问授权登录接口
                            .requestMatchers(HttpMethod.POST,"/coc/user/login").permitAll()
                            // 允许 SpringMVC 的默认错误地址匿名访问
                            .requestMatchers("/error").permitAll()
                            // 除上面外的所有请求全部需要鉴权认证
                            .anyRequest().authenticated();
                }))
                .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authenticationProvider());
        return http.build();

    }
}

这样过滤器就不会多次执行了

第二个原因

第二个原因是我在SpringSecurity的github找到的一篇issue中看到的,原贴点此

有一个人是这样说的

原文如下,大意就是permitAll对权限校验的fillter没有影响,Security先认证后授权,permitAll是授权部分

permitAll() has no effect on authentication filters. Spring Security processes authentication first and then authorization, and permitAll() is an authorization matter.

Things essentially happen in this order:

  1. Write Secure Headers, like X-XSS-Protection
  2. Create an Authentication statement (that's what the authentication filters are for)
  3. Decide if that Authentication is enough to allow the request (permitAll() always says "yes")

Step 2 is where a JWT filter would go since that's how you'd figure out what privileges (scopes for example) that token has. That question has to be asked before an authorization decision (permitAll()) can be made.

Note that just because a request is public, that doesn't mean the response won't be rendered differently if there is a user in context. Thus, the separation of these two concerns is important.

It's already been stated that WebSecurity is how to completely ignore certain endpoints. Note, though, that this means that Spring Security won't secure that request in any way (no Step 1, 2, or 3).

 第二个原因解决

在这篇2017年的issue中,一条2019年的回复提供了一个方法解决这个问题,这个方法是当时的一个配置项,但是到Security6中这个配置项更改了,我去官方api文档中找相似的api终于找到关于这个配置的说明

Callback interface for customizing WebSecurity. Beans of this type will automatically be used by WebSecurityConfiguration to customize WebSecurity. 

用于自定义WebSecurity的回调接口。WebSecurityConfiguration将自动使用此类型的Bean来自定义WebSecurity。

示例用法

 @Bean
 public WebSecurityCustomizer ignoringCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/ignore1", "/ignore2");
 }

 我将它用到了我的项目中,以下是我完整的SecurityConfig配置

package com.greenjiao.coc.config;

import com.greenjiao.coc.filter.JwtAuthenticationFilter;
import com.greenjiao.coc.mapper.UserMapper;
import com.greenjiao.coc.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Author Yan
 * @Date 2023/06/04 8:37
 * @Version 1.0
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private UserMapper userMapper;

    public JwtAuthenticationFilter authenticationJwtTokenFilter() {
        return new JwtAuthenticationFilter(userMapper,userDetailsService);
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        // 提供自定义loadUserByUsername
        authProvider.setUserDetailsService(userDetailsService);
        // 指定密码编辑器
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 禁用basic明文验证
                .httpBasic().disable()
                // 禁用默认登录页
                .formLogin().disable()
                // 禁用默认登出页
                .logout().disable()
                // 设置异常的EntryPoint,如果不设置,默认使用Http403ForbiddenEntryPoint
//                .exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(invalidAuthenticationEntryPoint))
                // 前后端分离是无状态的,不需要session了,直接禁用。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests((authorizeRequests -> {
                    authorizeRequests
                            // 允许所有OPTIONS请求
                            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                            // 允许直接访问授权登录接口
                            .requestMatchers(HttpMethod.POST,"/coc/user/login").permitAll()
                            // 允许 SpringMVC 的默认错误地址匿名访问
                            .requestMatchers("/error").permitAll()
                            // 除上面外的所有请求全部需要鉴权认证
                            .anyRequest().authenticated();
                }))
                .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authenticationProvider());
        return http.build();

    }
    @Bean
    public WebSecurityCustomizer ignoringCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/coc/user/login");
    }
}

项目中添加了这个配置后,忽略的路径会执行认证流程,并且不会再执行后续的过滤器了,就解决了我登录接口依旧走token校验过滤器的问题。

Security6的配置也更改了,最初我是看的这位的博客参考新配置

Spring Security 6 配置方法,废弃 WebSecurityConfigurerAdapter_markvivv的博客-CSDN博客

  • 16
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值