【Spring Security】使用 postman 請求一個有 spring security 保護的 api,登入失敗回傳 401 狀態碼 ,但使用前端後分離的 vue 請求同樣的api,卻是回傳 302 狀態碼

問題原因

Postman

Vue

這是因為 Spring Security 預設會將未通過認證的請求,重新導向至登錄頁面

解決方法

需要在 Spring Security 關閉重新導向,並回傳 401 狀態碼

新增CustomAuthenticationEntryPoint類別實作AuthenticationEntryPoint類別,並改寫commence()方法

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setStatus(HttpStatus.UNAUTHORIZED.value()); // 回傳 401 狀態碼
        response.getWriter().write("Unauthorized");
    }
}

在設定 Spring Security 類別注入CustomAuthenticationEntryPoint Bean

@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

回傳 SecurityFilterChain Bean 的方法增加 exceptionHandling()方法,並加入CustomAuthenticationEntryPoint Bean

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        return http
                // 設定 Session 的創建實例, 使用 http basic 認證時,創造 Session 和 Cookie
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
                .csrf(csrf -> csrf.disable())
                .httpBasic(Customizer.withDefaults())
                .formLogin(Customizer.withDefaults())
                .authorizeHttpRequests(request -> request
                        // 註冊功能
                        .requestMatchers("/", "/register").permitAll()
                        .requestMatchers("/memberLogin").authenticated()
                        // 權限
                        .requestMatchers("/hello").hasRole("ADMIN")
                        .requestMatchers("/helloWorld").hasRole("NORMAL_MEMBER")
                        .anyRequest().denyAll() // deny-by-default
                )
                .exceptionHandling(exceptionHandling ->
                                exceptionHandling
                                .authenticationEntryPoint(customAuthenticationEntryPoint)
                )
                .formLogin(formLogin ->
                        formLogin
                                .loginPage("/login")
                                .permitAll()
                )
                .logout(logout ->
                        logout
                                .permitAll()
                )
                .cors(Customizer.withDefaults()) // disable this line to reproduce the CORS 401
                .build();
    }

重啟後,前端認證失敗就不會再回傳 302 狀態碼了。

參考資料

  1. ChatGPT

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *