package com.xly.erp.common.config; import com.xly.erp.common.security.JwtAuthenticationFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; 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.HttpStatusEntryPoint; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * Spring Security 配置(docs/04 § 1.7)。 * *

REQ-USR-001 T3:无状态 JWT 认证;放行 /api/usr/login、swagger、actuator/health, * 其余需认证;注册 BCryptPasswordEncoder Bean;JwtAuthenticationFilter 挂在 * UsernamePasswordAuthenticationFilter 之前。

*/ @Configuration public class SecurityConfig { /** * 放行清单(无需 token 即可访问)。 * *

REQ-USR-001 放行 /api/usr/login;REQ-USR-004 T5 追加 /api/usr/companies(登录页版本下拉)。 * 抽为常量供单测断言与过滤链共用,避免清单漂移。

*/ public static final String[] PERMIT_ALL_PATHS = { "/api/usr/login", "/api/usr/companies", "/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**", "/actuator/health", }; /** * 密码哈希编码器(BCrypt),禁止明文存储 / 比对。 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling(eh -> eh .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) .authorizeHttpRequests(auth -> auth .requestMatchers(PERMIT_ALL_PATHS) .permitAll() .anyRequest().authenticated()) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }