Spring Boot 3.2 集成最新 Spring Security6 实战

Spring Boot 3.2 集成最新 Spring Security6 实战

码农世界 2024-05-22 后端 114 次浏览 0个评论

Spring Boot 3.2 集成最新 Spring Security6 实战

引言

虽然网上已经有很多对于Spring Security6用法的最近用法的介绍,但并没有一个比较完整的示例讲解我们应该如何把以前过时的方法更新到最新的用法。因此,我以自己项目中的security配置为例,提供一个已经验证过的springSecurityFilterChain配置示例,供大伙参考。

调用链路

下面是Spring Security常见Bean在处理请求的调用链路:

/**
 * 处理链路:
 *      login : CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthenticationManager(优先级高于  SecurityUserDetailsService)  -> AuthenticationSuccessHandler/AuthenticationFailHandler
 *      logout: CookieToHeadersFilter -> ScSecurityContextRepository -> LogoutHandler -> LogoutSuccessHandler
 *      未登录进行 url request: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> ScAuthenticationEntryPoint
 *      登录后进行url request: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> CookieToHeadersFilter(子线程, 可以在前面ScSecurityContextRepository更新token并重新设置请求头)-> 服务接口
 *      鉴权失败: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> ScAuthenticationEntryPoint -> ScAccessDeniedHandler
 */

各个Bean的实现并没有什么变化,不知道怎么写的可以参考这篇博客,里面的很详细:https://blog.csdn.net/yuan__once/article/details/127022613

过时的写法

在最新版的Spring Security6.x版本中,我们发现以前WebSecurityConfig的springSecurityFilterChain方法配置写法已经过时,包括我们熟悉的and()方法等,官方将在Spring Security7中移除这些过时的方法:

Spring Boot 3.2 集成最新 Spring Security6 实战

最新的写法

目前,官方采用Lambda的配置来替代传统的链式配置,示例如下:

Spring Boot 3.2 集成最新 Spring Security6 实战

可以看到,这种采用 lambda 表达式的写法能更加清晰地展示 Spring Security 在处理 url 请求的调用链路,不再像以前一样一条链路写到头,又臭又长还显得冗余。经过测试,这种写法之前过时的写法实现的调用链路是完全一致的。

(不枉我摸索半天T^T)

WebSecurityConfig完整代码参考

/**
 * WebSecurityConfig 核心配置
 * @Author: ZenSheep
 * @Date: 2024/2/1 18:34
 */
@Configuration
@EnableWebFluxSecurity
public class WebSecurityConfig {
    @Autowired
    CookieToHeadersFilter cookieToHeadersFilter;
    @Autowired
    ScSecurityContextRepository scSecurityContextRepository;
    @Autowired
    ScAuthorizationManager scAuthorizationManager;
    @Autowired
    ScPermitUrlConfig scPermitUrlConfig;
    @Autowired
    ScAccessDeniedHandler scAccessDeniedHandler;
    @Autowired
    ScAuthenticationEntryPoint scAuthenticationEntryPoint;
    @Autowired
    AuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    AuthenticationFailHandler authenticationFailHandler;
    @Autowired
    private LogoutHandler logoutHandler;
    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;
//    /**
//     *
//     * 处理链路:
//     *      login : CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthenticationManager(优先级高于  SecurityUserDetailsService)  -> AuthenticationSuccessHandler/AuthenticationFailHandler
//     *      logout: CookieToHeadersFilter -> ScSecurityContextRepository -> LogoutHandler -> LogoutSuccessHandler
//     *      未登录进行 url request: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> ScAuthenticationEntryPoint
//     *      登录后进行url request: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> CookieToHeadersFilter(子线程, 可以在前面ScSecurityContextRepository更新token并重新设置请求头)-> 服务接口
//     *      鉴权失败: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> ScAuthenticationEntryPoint -> ScAccessDeniedHandler
//     * @param http
//     * @return
//     */
//    @Bean
//    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
//        // 将Cookie写入Http请求头中,SecurityWebFiltersOrder枚举类定义了执行次序
//        http.addFilterBefore(cookieToHeadersFilter, SecurityWebFiltersOrder.HTTP_HEADERS_WRITER);
//
//        http
//            // 存储认证信息
//            .securityContextRepository(scSecurityContextRepository)
//            //请求拦截处理
//            .authorizeExchange(exchange -> exchange // 请求拦截处理
//                    .pathMatchers(scPermitUrlConfig.permit()).permitAll() // 默认放开的地址
//                    .pathMatchers(HttpMethod.OPTIONS).permitAll() // 放开的请求方法
//                    .anyExchange().access(scAuthorizationManager) // 其它的地址走后续验证
//            )
//            // 登录接口
//            .httpBasic()
//            .and()
//            .formLogin().loginPage("/api/v1/login") // 在ScAuthorizationManager的authenticate处理登录请求
//            .authenticationSuccessHandler(authenticationSuccessHandler) //认证成功
//            .authenticationFailureHandler(authenticationFailHandler) // 认证失败
//            .and()
//            .exceptionHandling().authenticationEntryPoint(scAuthenticationEntryPoint) // 未认证访问服务接口处理
//            .and()
//            .exceptionHandling().accessDeniedHandler(scAccessDeniedHandler) // 授权失败
//            .and()
//            // 登出接口
//            .logout().logoutUrl("/api/v1/logout")
//            .logoutHandler(logoutHandler) // 登出处理
//            .logoutSuccessHandler(logoutSuccessHandler)  // 登出成功处理
//            .and()
//            // 跨域配置
//            .cors()
//            .configurationSource(corsConfigurationSource())
//            // 关闭csrf防护,防止用户无法被认证
//            .and().csrf().disable();
//
//        return http.build();
//    }
    /**
     *
     * 处理链路:
     *      login : CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthenticationManager(优先级高于  SecurityUserDetailsService)  -> AuthenticationSuccessHandler/AuthenticationFailHandler
     *      logout: CookieToHeadersFilter -> ScSecurityContextRepository -> LogoutHandler -> LogoutSuccessHandler
     *      未登录进行 url request: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> ScAuthenticationEntryPoint
     *      登录后进行url request: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> CookieToHeadersFilter(子线程, 可以在前面ScSecurityContextRepository更新token并重新设置请求头)-> 服务接口
     *      鉴权失败: CookieToHeadersFilter -> ScSecurityContextRepository -> ScAuthorizationManager -> ScAuthenticationEntryPoint -> ScAccessDeniedHandler
     * @param http
     * @return
     */
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // 将Cookie写入Http请求头中,SecurityWebFiltersOrder枚举类定义了执行次序
        http.addFilterBefore(cookieToHeadersFilter, SecurityWebFiltersOrder.HTTP_HEADERS_WRITER);
        http
                // 存储认证信息
                .securityContextRepository(scSecurityContextRepository)
                //请求拦截处理
                .authorizeExchange(exchange -> exchange // 请求拦截处理
                        .pathMatchers(scPermitUrlConfig.permit()).permitAll() // 默认放开的地址
                        .pathMatchers(HttpMethod.OPTIONS).permitAll() // 放开的请求方法
                        .anyExchange().access(scAuthorizationManager) // 其它的地址走后续验证
                )
                // 登录接口
                .formLogin(form -> form
                        .loginPage("/api/v1/login") // 登录请求路径,在 ScAuthorizationManager 的 authenticate 处理登录请求
                        .authenticationSuccessHandler(authenticationSuccessHandler) // 认证成功处理
                        .authenticationFailureHandler(authenticationFailHandler) // 认证失败处理
                )
                // 处理认证/授权异常
                .exceptionHandling(exceptionHandlingSpec -> exceptionHandlingSpec
                        .authenticationEntryPoint(scAuthenticationEntryPoint) // 未认证访问服务接口处理
                        .accessDeniedHandler(scAccessDeniedHandler) // 授权失败处理
                )
                .logout(logoutSpec -> logoutSpec
                        .logoutUrl("/api/v1/logout") // 登出路径
                        .logoutHandler(logoutHandler) // 处理登出请求
                        .logoutSuccessHandler(logoutSuccessHandler) // 登出成功处理
                )
                // 关闭csrf防护,防止用户无法被认证
                .csrf(csrf -> csrf.disable())
                // 跨域配置
                .cors(cors -> cors.configurationSource(corsConfigurationSource()));
        return http.build();
    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许哪些网站的跨域请求
        corsConfiguration.addAllowedOriginPattern("*");
        // 允许所有请求方法
        corsConfiguration.addAllowedMethod("*");
        // 允许所有域,当请求头
        corsConfiguration.addAllowedHeader("*");
        // 允许携带 Authorization 头
        corsConfiguration.setAllowCredentials(true);
        // 允许全部请求路径
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
}

转载请注明来自码农世界,本文标题:《Spring Boot 3.2 集成最新 Spring Security6 实战》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,114人围观)参与讨论

还没有评论,来说两句吧...

Top