Spring OAuth2 SSO异常:无令牌仍可登录且注销后自动登录
解决Spring OAuth2 SSO注销后自动重登的问题
我之前也踩过OAuth2 SSO注销的坑,你的问题核心其实是注销操作只清理了授权服务器的令牌,但没处理SSO服务和授权服务的本地会话,也没同步清理浏览器端的认证Cookie,咱们一步步来解决:
问题拆解
- 注销后MongoDB令牌已清除,但刷新SSO页面自动登录:这是因为SSO服务器(
@EnableOAuth2Sso)自己维护了用户登录会话,只要本地Session还在,它会自动尝试重新获取令牌,甚至直接认为用户已登录。 - 授权服务器无令牌时SSO仍能登录:大概率是授权服务器的用户认证会话(比如登录时生成的Session)没失效,SSO跳转去授权时,授权服务器直接复用了旧会话完成认证。
具体解决方案
1. 清理SSO服务器的本地会话
在你的SSO注销端点里,除了调用授权服务器的令牌清除接口,一定要手动失效当前的HttpSession,同时清理浏览器端的SSO Cookie:
@GetMapping("/logout") public String logout(HttpServletRequest request, HttpServletResponse response) { // 1. 调用授权服务器的令牌撤销接口 String accessToken = (String) request.getSession().getAttribute("access_token"); if (accessToken != null) { HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(accessToken); restTemplate.postForObject("http://auth-server/oauth/revoke-token", headers, Void.class); } // 2. 失效SSO服务器的本地会话 request.getSession().invalidate(); SecurityContextHolder.clearContext(); // 3. 清理浏览器端的JSESSIONID Cookie Cookie sessionCookie = new Cookie("JSESSIONID", null); sessionCookie.setMaxAge(0); sessionCookie.setPath("/"); response.addCookie(sessionCookie); // 4. 重定向到授权服务器的注销页面,同步清理其会话 return "redirect:http://auth-server/logout?redirect_uri=http://sso-server/login"; }
2. 让授权服务器同步清理认证会话
授权服务器(@EnableAuthorizationServer)需要提供一个注销端点,不仅清除令牌,还要失效自身的登录Session:
@GetMapping("/logout") public String logout(HttpServletRequest request, @RequestParam String redirect_uri) { // 1. 根据当前登录用户清除所有关联令牌 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { // 从tokenStore中查询并删除该用户的所有access_token和refresh_token RefreshToken refreshToken = tokenStore.findRefreshTokenByUsername(auth.getName()); if (refreshToken != null) { tokenStore.removeAccessTokenUsingRefreshToken(refreshToken); tokenStore.removeRefreshToken(refreshToken); } } // 2. 失效授权服务器的本地会话 request.getSession().invalidate(); SecurityContextHolder.clearContext(); // 3. 清理浏览器端的授权服务器Cookie Cookie authSessionCookie = new Cookie("JSESSIONID", null); authSessionCookie.setMaxAge(0); authSessionCookie.setPath("/"); response.addCookie(authSessionCookie); // 4. 重定向回SSO的登录页面 return "redirect:" + redirect_uri; }
3. 强化SSO服务器的会话验证
在SSO的Security配置中,强制会话失效后跳转登录页,并且每次请求都验证令牌有效性,避免依赖本地Session直接登录:
@Configuration @EnableOAuth2Sso public class SsoSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .sessionManagement() .invalidSessionUrl("/login") // 会话失效后强制跳登录页 .maximumSessions(1) // 限制单用户单会话,避免旧会话残留 .and() .oauth2Client() .userInfoEndpoint() .userService(customUserInfoService); // 自定义用户信息服务,每次请求验证令牌有效性 } }
总结
注销操作必须是全链路的清理:授权服务器的令牌 + 授权服务器的登录会话 + SSO服务器的本地会话 + 浏览器端的认证Cookie,缺了任何一环都会出现自动重登的问题。按上面的步骤调整后,应该就能解决你的问题了。
内容的提问来源于stack exchange,提问作者J. Leander
相关产品推荐
相关产品推荐

