WangHan
2025-04-02 a8ba678a3fe5a39da2c732014cebbb66e408e97c
iplatform-base-security-consum/src/main/java/com/iplatform/security/config/WebSecurityConfig.java
@@ -37,6 +37,7 @@
import com.walker.web.security.DefaultSecurityMetadataSource;
import com.walker.web.security.ResourceLoadProvider;
import com.walker.web.token.JwtTokenGenerator;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@@ -45,6 +46,7 @@
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.ExceptionHandlingDsl;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -93,6 +95,13 @@
    }
    /**
 * HttpSecurity:忽略 antMatchers 中使用的端点的身份验证,其他安全功能将生效。<br></br>
 * WebSecurity:直接忽略也不会进行 CSRF xss等攻击保护。
 * @param http
 * @return
 * @throws Exception
 */
    /**
     * HttpSecurity:忽略 antMatchers 中使用的端点的身份验证,其他安全功能将生效。<br></br>
     * WebSecurity:直接忽略也不会进行 CSRF xss等攻击保护。
     * @param http
@@ -101,82 +110,89 @@
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 缓存 securityProperties 的结果,避免重复调用
        SecurityProperties securityProperties = this.securityProperties();
        DefaultUserDetailsService userDetailsService = userDetailsService(this.securityProperties(), this.userCacheProvider);
        DefaultUserDetailsService userDetailsService = userDetailsService(securityProperties, this.userCacheProvider);
        http.userDetailsService(userDetailsService);
        // CSRF禁用,因为不使用session
        http.csrf().disable();
        // ???
        http.headers().frameOptions().disable();
        // 注意:禁用CSRF需确保所有接口已通过其他方式保护
        http.csrf(csrf -> csrf.disable());
        // 登录行为由自己实现,参考 AuthController#login
        http.formLogin().disable().httpBasic().disable();
        // 禁用frameOptions以支持iframe嵌套
        // 替换弃用的 headers() 方法
        http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.disable()));
        // 匿名资源访问权限,返回无权限提示接口
        http.exceptionHandling().authenticationEntryPoint(failedAuthenticationEntryPoint())
                // 已认证用户无权限访问配置
                .accessDeniedHandler(this.accessDeniedHandler())
                .and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 禁用默认登录和HTTP Basic认证
        http.formLogin(formLogin -> formLogin.disable());
//        http.formLogin().loginProcessingUrl("/login")
//                        .failureHandler(this.authenticationFailureHandler());
        // 注意:这里不能配置上面的登录,否则就不会执行自己实现的/login方法。2022-11-11
        http.logout().logoutUrl("/logout").logoutSuccessHandler(this.logoutSuccessHandler()).permitAll();
        // 异常处理配置
        http.exceptionHandling(exceptionHandling -> exceptionHandling
                .authenticationEntryPoint(failedAuthenticationEntryPoint())
                .accessDeniedHandler(this.accessDeniedHandler()));
        // 匿名访问集合,2022-11-07
        List<String> anonymousList = this.securityProperties().getAnonymousList();
        if(!StringUtils.isEmptyList(anonymousList)){
            http.authorizeHttpRequests().antMatchers(anonymousList.toArray(new String[]{})).permitAll();
        }
//        http.authorizeHttpRequests().antMatchers("/login", "/register", "/captchaImage", "/test/**").permitAll();
//        http.authorizeHttpRequests().antMatchers("/static/**", "/test/**").permitAll();
//        http.authorizeHttpRequests().antMatchers("/security/**").hasAuthority("query_user");
        // 基于token,所以不需要session
        http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        // 2023-03-21 注释掉,调试activiti7时发现和下面重复,
        // http.addFilterBefore(securityInterceptor(), FilterSecurityInterceptor.class);
        /*http.authorizeHttpRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>(){
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                object.setAccessDecisionManager(accessDecisionManager());//决策管理器
                object.setSecurityMetadataSource(securityMetadataSource());//安全元数据源
                return object;
            }
        });*/
        // 登出配置
        http.logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessHandler(this.logoutSuccessHandler())
                .permitAll());
        // 2023-01-28 配置自定义认证提供者(密码验证用)
        http.authenticationProvider(this.authenticationProvider(userDetailsService, securityProperties()));
        // 配置匿名访问权限
        configureAnonymousAccess(http, securityProperties);
        // 配置自定义认证提供者
        http.authenticationProvider(this.authenticationProvider(userDetailsService, securityProperties));
        // 所有请求都需要认证
        http.authorizeHttpRequests().anyRequest().authenticated();
        // 使用自定义动态拦截器,拦截所有权限请求,2022-11-02
        http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests.anyRequest().authenticated());
        // 添加自定义动态拦截器
        http.addFilterBefore(securityInterceptor(), FilterSecurityInterceptor.class);
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // token拦截过滤器,2022-11-02
        // 必须在这里添加拦截,不能放在'FilterSecurityInterceptor'之后,因为如果放在之后,那么就无法获得用户信息,从而无法
        // 获得用户所具有的权限角色集合:roleIdList。2022-11-14(2)
        // 添加JWT认证过滤器
        http.addFilterBefore(jwtAuthenticationTokenFilter(userDetailsService), UsernamePasswordAuthenticationFilter.class);
//        http.addFilterBefore(jwtAuthenticationTokenFilter(), DefaultAuthenticationFilter.class);
        // 尝试让jwt在URL权限之后才拦截, 2022-11-14(1)
        // 注意:以上 UsernamePasswordAuthenticationFilter 需要去掉才能生效
//        http.addFilterAfter(jwtAuthenticationTokenFilter(), FilterSecurityInterceptor.class);
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        if(this.securityProperties().isCorsEnabled()){
            // 解决跨域过滤器,2022-11-06
            http.addFilterBefore(this.corsFilter().getFilter(), JwtAuthenticationTokenFilter.class);
            // 未知?2022-11-11
            http.addFilterBefore(this.corsFilter().getFilter(), LogoutFilter.class);
        } else {
            System.out.println("不添加跨域过滤器: ");
        }
        // 配置跨域过滤器
        configureCorsFilter(http, securityProperties);
        return http.build();
    }
    /**
     * 配置匿名访问权限
     */
    private void configureAnonymousAccess(HttpSecurity http, SecurityProperties securityProperties) throws Exception {
        List<String> anonymousList = securityProperties.getAnonymousList();
        if (!CollectionUtils.isEmpty(anonymousList)) {
            http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
                    .requestMatchers(anonymousList.toArray(new String[0])).permitAll());
        }
    }
    /**
     * 配置跨域过滤器
     */
    private void configureCorsFilter(HttpSecurity http, SecurityProperties securityProperties) throws Exception {
        if (securityProperties.isCorsEnabled()) {
            CorsFilter corsFilter = this.corsFilter().getFilter();
            if (corsFilter != null) {
                http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
                        .addFilterBefore(corsFilter, LogoutFilter.class);
                logger.info("跨域过滤器已启用");
            } else {
                logger.warn("跨域过滤器未正确初始化");
            }
        } else {
            logger.info("跨域过滤器未启用");
        }
    }
    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用
     * @param authenticationConfiguration
     * @return