PreAuthorize验证失败返回404而非401及登录页控制代码问题
Technical Support for Your Spring Security & Controller Questions
1. Fixing PreAuthorize Annotation Returning 404 Instead of 401
When @PreAuthorize validation fails, it should normally throw an AccessDeniedException (which maps to 403 by default), but if you’re seeing a 404 instead, here are the most common fixes to try:
- Double-check your endpoint mapping: First confirm the URL you’re testing is correctly mapped and accessible when the user is authorized. A mistyped path in
@GetMapping/@PostMappingwill return 404 regardless of authorization rules. - Enable method-level security: Ensure your Spring Security config class includes
@EnableGlobalMethodSecurity(prePostEnabled = true)(or@EnableMethodSecurityfor Spring Security 6+). Without this, the@PreAuthorizeannotation is completely ignored, which can lead to unexpected behavior if other security rules are conflicting. - Adjust exception handling: If your app has a custom error handler that’s incorrectly mapping
AccessDeniedExceptionto 404, you’ll need to override this. For REST controllers, add a global exception handler:
For MVC controllers, you can configure Spring Security’s exception handling directly in your security config:@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(AccessDeniedException.class) public ResponseEntity<String> handleUnauthorizedAccess(AccessDeniedException ex) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("You don't have permission to access this resource"); } }@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .accessDeniedHandler((request, response, ex) -> { response.setStatus(HttpStatus.UNAUTHORIZED.value()); // Optionally forward to a custom 401 page // request.getRequestDispatcher("/error/401").forward(request, response); }); } } - Resolve URL-based security conflicts: If you have overlapping URL-level security rules (like
http.authorizeRequests().antMatchers("/admin/**").authenticated()) and method-level@PreAuthorizerules, this can cause unexpected status codes. Ensure your rules are consistent and don’t contradict each other.
2. Enhancing IndexController for Login Status-Based Page Routing
Your IndexController has a solid foundation with page constants, but to dynamically return the right page based on login status, here’s how to implement the logic cleanly:
Option 1: Check Authentication via SecurityContextHolder
@PropertySource(value = "classpath:securityConfig.properties", ignoreResourceNotFound = true) @Controller public class IndexController { private static final String LOGIN_PAGE = "login"; private static final String HOME_PAGE = "home"; private static final String LOBBY_PAGE = "lobby"; private static final String FORGOT_USER_PAGE = "forgotUserName"; private static final String FORGOT_PASSWORD_PAGE = "forgotPassword"; @GetMapping("/") public String index() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); // Verify user is authenticated (and not the anonymous placeholder) if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken)) { // Return the appropriate page for logged-in users (adjust based on your needs) return LOBBY_PAGE; } else { // Redirect to login for unauthenticated users return LOGIN_PAGE; } } // Add mappings for other pages @GetMapping("/forgot-password") public String showForgotPasswordPage() { return FORGOT_PASSWORD_PAGE; } }
Option 2: Use @AuthenticationPrincipal for Cleaner Parameter Injection
If you’re using Spring Security 4.2+, you can inject the authenticated user directly into your controller method:
@GetMapping("/") public String index(@AuthenticationPrincipal UserDetails userDetails) { if (userDetails != null) { // Use userDetails to check roles or user-specific data if needed return HOME_PAGE; } else { return LOGIN_PAGE; } }
Additional Tips:
- Leverage view-side security expressions: If you’re using Thymeleaf, you can control content visibility directly in your templates without handling everything in the controller:
<div sec:authorize="isAuthenticated()"> <a href="/lobby">Enter Lobby</a> </div> <div sec:authorize="!isAuthenticated()"> <a href="/login">Sign In</a> </div> - Configure post-login redirects: Make sure your Spring Security config redirects users to the correct page after successful login:
@Override protected void configure(HttpSecurity http) throws Exception { http .formLogin() .loginPage("/login") .defaultSuccessUrl("/lobby", true); // Force redirect to lobby after login } - Externalize page names: Instead of hardcoding constants, use
@Valueto inject page names from yoursecurityConfig.properties:@Value("${pages.login:login}") private String loginPage;
内容的提问来源于stack exchange,提问作者Prateek Narendra
相关产品推荐
相关产品推荐

