xkcoding/spring-boot-demo · error · ResourceAccessException

请求错误,用户尚未登录

Error message

请求错误,用户尚未登录

What it means

Thrown by the OAuth logout endpoint when the Principal parameter is null — meaning no authenticated user session exists at the time of the GET /oauth/logout request. The exception type used is ResourceAccessException (Spring's web-client I/O exception), which is semantically wrong for an authentication-state problem; it should be an auth-related exception. This mismatch means Spring's exception handling may route it incorrectly.

Source

Thrown at demo-oauth/oauth-authorization-server/src/main/java/com/xkcoding/oauth/controller/Oauth2Controller.java:45

     *
     * @return view
     */
    @GetMapping("/login")
    public String loginView() {
        return "login";
    }

    /**
     * 退出登录
     *
     * @param redirectUrl 退出完成后的回调地址
     * @param principal   用户信息
     * @return 结果
     */
    @GetMapping("/logout")
    public ModelAndView logoutView(@RequestParam("redirect_url") String redirectUrl, Principal principal) {
        if (Objects.isNull(principal)) {
            throw new ResourceAccessException("请求错误,用户尚未登录");
        }
        ModelAndView view = new ModelAndView();
        view.setViewName("logout");
        view.addObject("user", principal.getName());
        view.addObject("redirectUrl", redirectUrl);
        return view;
    }

}

View on GitHub (pinned to 87a142f960)

Solutions

  1. Redirect unauthenticated users to the login page instead of throwing an exception.
  2. Replace ResourceAccessException with an appropriate auth exception (e.g., BadCredentialsException or a custom UnauthorizedException) to match the semantic.
  3. Add Spring Security configuration to require authentication on /oauth/logout so the endpoint is never reached without a Principal.

Example fix

// before
if (Objects.isNull(principal)) {
    throw new ResourceAccessException("请求错误,用户尚未登录");
}

// after — redirect to login instead of throwing a client-access exception
if (Objects.isNull(principal)) {
    ModelAndView view = new ModelAndView();
    view.setViewName("redirect:/oauth/login");
    return view;
}
Defensive patterns

Strategy: validation

Validate before calling

// Check authentication state before calling logoutView
// In Spring Security config, require authentication on /oauth/logout:
// .antMatchers("/oauth/logout").authenticated()
// This ensures Principal is never null at the controller.

// Alternatively, check in a filter:
if (SecurityContextHolder.getContext().getAuthentication() == null ||
    !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
    // redirect to login
}

Try / catch

// In a @ControllerAdvice handler — note ResourceAccessException is semantically wrong here
@ExceptionHandler(ResourceAccessException.class)
public String handleResourceAccessException(ResourceAccessException e) {
    log.warn("Resource access error: {}", e.getMessage());
    return "redirect:/oauth/login";
}

Prevention

When it happens

Trigger: Navigating to GET /oauth/logout?redirect_url=... without an active OAuth session (Principal injected by Spring Security is null). This happens when the session expired, the user was never authenticated, or the security context was cleared.

Common situations: Session timeout before clicking logout; accessing the logout URL directly without logging in first; Spring Security session management misconfigured so Principal is not injected; browser cache serving the logout link post-expiry.

Related errors


AI-assisted analysis of xkcoding/spring-boot-demo@87a142f960 (2026-08-14). Data as JSON: /api/errors/3e30803584df411c. Report an issue: GitHub.