diff --git a/src/main/java/com/waterquality/projectmanagement/config/Http401UnauthorizedEntryPoint.java b/src/main/java/com/waterquality/projectmanagement/config/Http401UnauthorizedEntryPoint.java new file mode 100644 index 0000000..1396b77 --- /dev/null +++ b/src/main/java/com/waterquality/projectmanagement/config/Http401UnauthorizedEntryPoint.java @@ -0,0 +1,19 @@ +package com.waterquality.projectmanagement.config; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import java.io.IOException; + +public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) + throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 返回 401 状态码 + response.setContentType("application/json"); + response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"You are not authenticated.\"}"); + } +} diff --git a/src/main/java/com/waterquality/projectmanagement/config/SecurityConfig.java b/src/main/java/com/waterquality/projectmanagement/config/SecurityConfig.java index 829d99d..57fc342 100644 --- a/src/main/java/com/waterquality/projectmanagement/config/SecurityConfig.java +++ b/src/main/java/com/waterquality/projectmanagement/config/SecurityConfig.java @@ -2,6 +2,8 @@ package com.waterquality.projectmanagement.config; import com.waterquality.projectmanagement.repository.EmployeeRepository; import jakarta.servlet.Filter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.apache.catalina.filters.CorsFilter; import org.springframework.context.annotation.Bean; @@ -19,6 +21,7 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; @@ -37,22 +40,45 @@ public class SecurityConfig { private final EmployeeRepository employeeRepository; private final JwtTokenProvider jwtTokenProvider; + +// @Bean +// public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { +// http +// .csrf(csrf -> csrf.disable()) +// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) +// .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 配置 CORS +// .authorizeHttpRequests(authz -> authz +// .requestMatchers("/api/auth/**").permitAll() +// .requestMatchers("/static/**","/assets/**").permitAll() +// .requestMatchers("/", "/login","/index.html","/favicon.svg").permitAll() // 确保允许访问根路径和登录路径 +// .anyRequest().authenticated()) +// .authenticationProvider(authenticationProvider()) +// .addFilterBefore((Filter) new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); +// +// return http.build(); +// } + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 配置 CORS + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authz -> authz - .requestMatchers("/api/auth/**").permitAll() - .requestMatchers("/static/**","/assets/**").permitAll() - .requestMatchers("/", "/login","/index.html","/favicon.svg").permitAll() // 确保允许访问根路径和登录路径 - .anyRequest().authenticated()) + .requestMatchers("/api/auth/**").permitAll() // 允许认证相关接口无需鉴权 + .requestMatchers("/api/**").authenticated() // 只有 /api/** 下的路由需要鉴权 + .requestMatchers("/static/**", "/css/**", "/js/**", "/assets/**", "/favicon.ico").permitAll() // 显式放通静态资源 + .requestMatchers("/", "/index.html").permitAll() // 放通 SPA 的入口页面 + .anyRequest().permitAll()) // 其他所有请求都无需鉴权 + .exceptionHandling(exception -> exception + .authenticationEntryPoint(new Http401UnauthorizedEntryPoint())) .authenticationProvider(authenticationProvider()) - .addFilterBefore((Filter) new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); return http.build(); } + + @Bean public CorsConfigurationSource corsConfigurationSource() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();