SpringSecurity6 全局认证,http.authenticationManager(authenticationManager(http));

SpringSecurity 增加全局认证问题。
在SpringBoot2.7.9和 SpringSecurity5.7.7情况下:以下配置可以生效没有问题


@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    @Resource
    SpringSecurityAdminConfig springSecurityAdminConfig;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private DefaultPasswordEncoder defaultPasswordEncoder;
    @Resource
    private AuthenticationEntryPointIHandler authenticationEntryPointIHandler;
    @Resource
    private SpringSecurityMobileConfig springSecurityMobileConfig;
    @Resource
    private TokenLogoutHandler tokenLogoutHandler;
    @Resource
    private AccessDeniedImplHandler accessDeniedImplHandler;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                //禁用它,因为前后端分离不需要
                .csrf(AbstractHttpConfigurer::disable)
                //禁用session
                .sessionManagement(AbstractHttpConfigurer::disable)

                //设置无需权限可访问的链接
                .authorizeHttpRequests(auhtor -> auhtor
                                .antMatchers("/security/captcha", "/security/login", "/security/getaMobileCode",
                                        "/api/ums/user/sendMobileCode/**", "/three/sendMobileCode",
                                        "/api/ums/user/loginByMobile", "/ums/user/getByMobile/**",
//
//                                "**/doc.html", "/swagger/**", "/v2/api-docs", "/doc.html",
//                                "/webjars/**", "/druid/**", "/login", "/swagger/**", "/v2/api-docs", "/doc.html", "/swagger-resources/**",
//                                "/",
                                        " /**/*.html",
                                        "/**/*.css",
                                        "/**/*.js",
                                        "/**/api-docs/**"
                                ).permitAll()
                                .anyRequest().authenticated()
                );

        // 设置退出路径
        http.logout().logoutUrl("/security/logout")
                //注销成功后,回到首页
                .logoutSuccessUrl("/")
                //设置退出处理方法
                .addLogoutHandler(tokenLogoutHandler);

        //添加用户名登录配置类
        http.apply(springSecurityAdminConfig);
        //注入新的AuthenticationManager 认证管理
        http.authenticationManager(authenticationManager(http));


        //添加手机登录配置类型
        http.apply(springSecurityMobileConfig);


        //认证异常处理器
        http.exceptionHandling(ex -> {
            ex.authenticationEntryPoint(authenticationEntryPointIHandler).accessDeniedHandler(accessDeniedImplHandler);
        });

        return http.build();
    }

    /**
     * 构造一个AuthenticationManager,使用自定义的userDetailsService和passwordEncoder
     */
    @Bean
    AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {

        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManagerBuilder.class)
                //设置查询service
                .userDetailsService(userDetailsService)
                //设置密码处理
                .passwordEncoder(defaultPasswordEncoder)
                .and()
                .build();
        return authenticationManager;
    }
}

将项目升级到SpringBoot3.0.2后,SpringSecurity6.0.1后。
红色区域报错,切无法访问。

img

报错内容为:直接调用使用 @Bean 注解的方法。请改用依赖项注入。
虽能正常启动。但前端访问直接报:6Cross-Origin Read Blocking (CORB) 已屏蔽 MIME 类型为 text/html 的跨域响应
此处该如何解决?

"直接调用使用 @Bean 注解的方法。请改用依赖项注入" 这个只是IDEA的代码检查提示,因为你这个方法已经有@Bean注解了,所以可以直接通过依赖的方式注入而不是去调用这个方法。

以下回答参考GPT并且由Bony-整理:
根据报错信息来看,似乎是因为在 Spring Boot 3.x 以及 Spring Security 6.x 中,调用 authenticationManager() 方法时使用了 http.getSharedObject(AuthenticationManagerBuilder.class),而在 Spring Security 6.x 中,http.getSharedObject() 已经被废弃了,所以建议您改为使用 http.getSharedObject(AuthenticationManager.class)

另外,关于跨域问题,建议您检查一下是否已经添加了跨域配置。如果您是使用 Spring Security 中的 CorsFilter 来进行跨域配置的话,可以尝试将其移动到 FilterRegistrationBean 中进行配置,然后在 SecurityConfig 中通过 @Order(Ordered.HIGHEST_PRECEDENCE) 来设置其优先级,以确保跨域配置优先于 Spring Security 过滤器的执行。

举个例子,可以这样配置:

@Configuration
public class CorsConfig {

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", corsConfiguration);
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>(corsFilter);
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registrationBean;
    }
    
}

希望以上信息对您有所帮助。

引用chatGPT作答,在 Spring Security 6.0 中,关于配置 SecurityFilterChain 已经有所变化。现在,您需要在 SecurityConfig 类中注入一个类型为 SecurityFilterChainFactory 的 bean,并使用它来创建 SecurityFilterChain。

另外,关于报错 "直接调用使用 @Bean 注解的方法。请改用依赖项注入",可能是因为 Spring Security 6.0 引入了一个新的特性,该特性禁止直接调用使用 @Bean 注解的方法,而应该使用构造函数注入或依赖项注入的方式。

以下是更新后的代码示例:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    @Resource
    SpringSecurityAdminConfig springSecurityAdminConfig;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private DefaultPasswordEncoder defaultPasswordEncoder;
    @Resource
    private AuthenticationEntryPointIHandler authenticationEntryPointIHandler;
    @Resource
    private SpringSecurityMobileConfig springSecurityMobileConfig;
    @Resource
    private TokenLogoutHandler tokenLogoutHandler;
    @Resource
    private AccessDeniedImplHandler accessDeniedImplHandler;
    @Resource
    private SecurityFilterChainFactory securityFilterChainFactory;

    @Bean
    public SecurityFilterChain securityFilterChain() throws Exception {
        return securityFilterChainFactory.createHttpSecurityFilterChain(HttpSecurity.http());
    }

    // 其他代码...
}

对于前端报错 "Cross-Origin Read Blocking (CORB) 已屏蔽 MIME 类型为 text/html 的跨域响应",这通常是因为后端返回的响应类型是 text/html,而不是应该是 application/json。您可以在前端代码中添加一些配置,以避免这个错误。例如,对于 Vue.js,您可以添加以下配置:

axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.headers.common['Content-Type'] = 'application/json';

这应该可以解决前端报错的问题。

该回答引用ChatGPT
根据报错提示,需要使用依赖项注入的方式来获取 `authenticationManager`,而不是在 `SecurityConfig` 中使用 `@Bean` 注解直接声明 `authenticationManager` 方法。可以将此方法注入到 `WebSecurityConfigurerAdapter` 子类中,再在 `HttpSecurity` 中调用。

以下是修改后的 `SecurityConfig` 代码示例:


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private SpringSecurityAdminConfig springSecurityAdminConfig;

@Autowired
private UserDetailsService userDetailsService;

@Autowired
private DefaultPasswordEncoder defaultPasswordEncoder;

@Autowired
private AuthenticationEntryPointIHandler authenticationEntryPointIHandler;

@Autowired
private SpringSecurityMobileConfig springSecurityMobileConfig;

@Autowired
private TokenLogoutHandler tokenLogoutHandler;

@Autowired
private AccessDeniedImplHandler accessDeniedImplHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {
http
//禁用它,因为前后端分离不需要
.csrf(AbstractHttpConfigurer::disable)
//禁用session
.sessionManagement(AbstractHttpConfigurer::disable)

//设置无需权限可访问的链接
.authorizeHttpRequests(auhtor -> auhtor
.antMatchers("/security/captcha", "/security/login", "/security/getaMobileCode",
"/api/ums/user/sendMobileCode/**", "/three/sendMobileCode",
"/api/ums/user/loginByMobile", "/ums/user/getByMobile/**",
//
// "/**/doc.html", "/swagger/**", "/v2/api-docs", "/doc.html",
// "/webjars/**", "/druid/**", "/login", "/swagger/**", "/v2/api-docs", "/doc.html", "/swagger-resources/**",
// "/",
" /**/*.html",
"/**/*.css",
"/**/*.js",
"/**/api-docs/**"
).permitAll()
.anyRequest().authenticated()
);

// 设置退出路径
http.logout().logoutUrl("/security/logout")
//注销成功后,回到首页
.logoutSuccessUrl("/")
//设置退出处理方法
.addLogoutHandler(tokenLogoutHandler);

//添加用户名登录配置类
http.apply(springSecurityAdminConfig);
//注入新的AuthenticationManager 认证管理
http.authenticationManager(authenticationManager());


//添加手机登录配置类型
http.apply(springSecurityMobileConfig);


//认证异常处理器
http.exceptionHandling(ex -> {
ex.authenticationEntryPoint(authenticationEntryPointIHandler).accessDeniedHandler(accessDeniedImplHandler);
});
}

/**
* 构造一个AuthenticationManager,使用自定义的userDetailsService和passwordEncoder
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected UserDetailsService userDetailsService() {
return userDetailsService;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(defaultPasswordEncoder);
}
}


同时,针对 CORS 报错,可以配置跨域访问,可在 `configure(HttpSecurity http)` 方法中添加以下代码(注:需根据实际情况修改 `allowedOrigins` 和 `allowedMethods` 值):


http.cors().configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
return config;
});


希望能对你有所帮助。