package com.tourguide.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.tourguide.common.api.ApiResponse; import com.tourguide.security.JwtAuthenticationFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; @Configuration public class SecurityConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtFilter, ObjectMapper objectMapper) throws Exception { http.csrf().disable() .cors().and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .exceptionHandling() .authenticationEntryPoint((request, response, exception) -> { response.setStatus(401); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); objectMapper.writeValue(response.getWriter(), ApiResponse.failure(401, "请先登录")); }) .accessDeniedHandler((request, response, exception) -> { response.setStatus(403); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); objectMapper.writeValue(response.getWriter(), ApiResponse.failure(403, "无权访问")); }).and() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers("/api/admin/auth/login", "/api/mini/auth/wechat-phone-login", "/api/guide/auth/login", "/api/guide/auth/password-login", "/api/guide/auth/sms-code", "/api/health").permitAll() .antMatchers("/api/guide/registration/**").permitAll() .antMatchers("/base/dougong/**").permitAll() .antMatchers(HttpMethod.GET, "/uploads/avatars/**").permitAll() .antMatchers("/uploads/guide-applications/**").hasRole("ADMIN") .antMatchers(HttpMethod.GET, "/api/mini/scenic-spots", "/api/mini/scenic-spots/**").permitAll() .antMatchers(HttpMethod.GET, "/api/mini/agreements", "/api/mini/agreements/**").permitAll() .antMatchers(HttpMethod.GET, "/api/guide/agreements", "/api/guide/agreements/**").permitAll() .antMatchers("/api/admin/**").hasRole("ADMIN") .antMatchers("/api/mini/**").hasRole("MINI_USER") .antMatchers("/api/guide/**").hasRole("GUIDE_USER") .anyRequest().authenticated().and() .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOriginPatterns(Collections.singletonList("*")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Collections.singletonList("*")); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }