活动介绍

@Bean protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity // CSRF禁用,因为不使用session .csrf(csrf -> csrf.disable()) // 禁用HTTP响应标头 .headers((headersCustomizer) -> { headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); }) // 认证失败处理类 .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) // 基于token,所以不需要session .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 注解标记允许匿名访问的url .authorizeHttpRequests((requests) -> { permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); // 对于登录login 注册register 验证码captchaImage 允许匿名访问 requests.antMatchers("/login", "/register", "/captchaImage").permitAll() .antMatchers("/app/*").permitAll() .antMatchers("/app/minLogin").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated(); }) // 添加Logout filter .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) // 添加JWT filter .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) // 添加CORS filter .addFilterBefore(corsFilter,

时间: 2025-03-20 21:15:45 浏览: 81
### 配置Spring Security Filter Chain以支持无状态会话管理、CSRF禁用及自定义过滤器 在Spring Security中,可以通过配置`HttpSecurity`对象来定制化安全策略。以下是关于如何实现无状态会话管理、禁用CSRF保护以及添加自定义过滤器(如JWT和CORS)的具体方法。 #### 1. 实现无状态会话管理 为了实现无状态会话管理,在Spring Security配置类中的`configure(HttpSecurity http)`方法里,应设置会话管理策略为`SessionCreationPolicy.STATELESS`。这表示不会创建或使用任何服务器端的会话数据[^3]。 ```java http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); ``` 此配置确保每次请求都独立于其他请求,并且不依赖于传统的基于Cookie的会话机制。 #### 2. 禁用CSRF防护 由于无状态应用通常不需要浏览器环境下的交互操作,因此可以考虑禁用CSRF防护功能。然而需要注意的是,如果应用程序确实存在跨站点请求伪造的风险,则不应简单地关闭该特性。对于大多数API服务而言,当采用令牌认证方式时,可以直接禁用CSRF: ```java http.csrf().disable(); ``` 上述代码片段展示了如何通过调用`.csrf().disable()`方法来移除默认启用的CSRF防御措施[^2]。 #### 3. 添加自定义过滤器 - JWT与CORS ##### (a) 自定义JWT过滤器 要集成JWT身份验证逻辑至现有的Spring Security框架下,需构建一个新的过滤器并将其插入到标准的安全过滤链条之前。例如,可以在用户名密码验证之后立即执行我们的JWT解析过程: ```java public class JwtRequestFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); try { username = jwtUtil.extractUsername(jwt); // 假设有一个jwt工具类用于解码token } catch (Exception e){ logger.error("Error while extracting token",e); } } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtUtil.validateToken(jwt, userDetails)) { // 验证token有效性 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } chain.doFilter(request, response); } } ``` 接着将这个新的过滤器加入到Spring Security配置当中: ```java @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.cors() .and() .csrf().disable() .authorizeHttpRequests((requests) -> requests .requestMatchers("/authenticate").permitAll() .anyRequest().authenticated()) .addFilterBefore(new JwtRequestFilter(), UsernamePasswordAuthenticationFilter.class) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); return http.build(); } ``` 这里我们把`JwtRequestFilter`放置到了`UsernamePasswordAuthenticationFilter`前面的位置上[^4]。 ##### (b) 处理CORS问题 最后一步是解决可能存在的跨域资源共享(CORS)问题。同样是在同一个`securityFilterChain` bean定义期间完成这项工作: ```java http.cors(cors -> cors.configurationSource(corsConfigurationSource())); // 或者更简单的形式如下所示: http.cors().configurationSource(request -> { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); config.addAllowedMethod("*"); config.addAllowedHeader("*"); return config; }); ``` 以上步骤共同构成了一个完整的解决方案,能够满足题目所提到的需求——即配置Spring Security filter chain来进行无状态会话管理和相关增强功能的支持。 ---
阅读全文

相关推荐

流式接口访问报错Unable to handle the Spring Security Exception because the response is already committed., 这是我的相关配置@Bean protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity // CSRF禁用,因为不使用session .csrf(AbstractHttpConfigurer::disable) // 禁用HTTP响应标头 .headers((headersCustomizer) -> headersCustomizer.cacheControl(HeadersConfigurer.CacheControlConfig::disable).frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) // 基于token,所以不需要session .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 注解标记允许匿名访问的url .authorizeHttpRequests((requests) -> { // 对于登录login 注册register 验证码captchaImage 允许匿名访问 requests.requestMatchers("/login", "/register", "/captchaImage", "/getRegisterCondition").permitAll() // 静态资源,可匿名访问 .requestMatchers(HttpMethod.GET, "/profile/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated(); }) // 认证失败处理类 .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) // 添加Logout filter .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) // 添加JWT filter .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) // 添加CORS filter .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) .addFilterBefore(corsFilter, LogoutFilter.class); return httpSecurity.build(); } 下面的是接口代码 @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream(@RequestBody ChatMessage chatMessage) { if (StringUtils.isEmpty(chatMessage.getMessage())) { throw new ServiceException("消息不能为空"); } return chatModel.stream(chatMessage.getMessage()) .doOnSubscribe(s -> System.out.println("连接成功")) .onErrorResume(e -> Flux.just("连接失败")) .doFinally(f -> System.out.println("连接关闭")); }

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.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public JwtTokenProvider jwtTokenProvider() { return new JwtTokenProvider(); } @Autowired private JwtTokenProvider jwtTokenProvider; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class) .authorizeRequests() .antMatchers("/api/").authenticated() .anyRequest().permitAll(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers(HttpMethod.OPTIONS, "/"); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); } }报错信息:Cannot resolve symbol 'JwtTokenFilter',如何创建一个JwtTokenFilter类,让代码不报错

package com.kuafu.login.config; import com.kuafu.login.handle.AuthenticationEntryPointImpl; import com.kuafu.login.handle.JwtAuthenticationTokenFilter; import com.kuafu.login.handle.LogoutSuccessHandlerImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.util.Map; @Slf4j @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @ConditionalOnProperty(prefix = "login", name = "enable") public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private ApplicationContext applicationContext; /** * 认证失败处理类 */ @Autowired private AuthenticationEntryPointImpl unauthorizedHandler; @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token认证过滤器 */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping; @Override protected void configure(HttpSecurity http) throws Exception { final Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods(); ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http .cors().and() .csrf().disable() // 禁用HTTP响应标头 .headers().cacheControl().disable().and() // 认证失败处理类 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests(); registry .antMatchers("/doc.html").permitAll() .antMatchers("/login/**").permitAll() .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js","/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() .antMatchers("/**/*.png","/**/*.jpg","/**/*.svg","/**/*.ico").permitAll() .antMatchers("/").permitAll(); handlerMethods.forEach((info, method) -> { registry.antMatchers(info.getPatternsCondition().getPatterns().toArray(new String[0])).authenticated(); }); registry.and().headers().frameOptions().disable(); // 添加Logout filter http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * 身份认证接口 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { Map<String, AuthenticationProvider> providerMap = applicationContext.getBeansOfType(AuthenticationProvider.class); for (Map.Entry<String, AuthenticationProvider> entry : providerMap.entrySet()) { log.info("=============== loading provider {}", entry.getKey()); auth.authenticationProvider(entry.getValue()); } } @Bean public HttpFirewall allowDoubleSlashFirewall() { StrictHttpFirewall firewall = new StrictHttpFirewall(); firewall.setAllowUrlEncodedDoubleSlash(true); return fi