【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 狀態碼了。
參考資料
- ChatGPT