Spring Boot集成Github OAuth2登录后用户匿名,如何实现无会话Token交互
我在Spring Boot项目中接入Github OAuth授权,登录成功后用户仍处于匿名状态。
代码与现象
HomeController代码
@RestController @RequestMapping(path = "/") public class HomeController { @GetMapping public String home() { return "Hello, %s".formatted(SecurityContextHolder.getContext().getAuthentication().getName()); } }
输出结果
Hello, anonymousUser
安全配置与application.yaml
SecurityFilterChain Bean
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, Converter<Jwt, UsernamePasswordAuthenticationToken> converter) throws Exception { http .authorizeHttpRequests(authorize -> authorize .anyRequest() .permitAll() ) .csrf(AbstractHttpConfigurer::disable) .cors(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .oauth2Login(withDefaults()) .oauth2ResourceServer(configurer -> configurer .jwt((jwt) -> jwt.jwtAuthenticationConverter(converter)) ) .sessionManagement(configurer -> configurer .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .exceptionHandling(configurer -> configurer .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) .accessDeniedHandler(new BearerTokenAccessDeniedHandler()) ); return http.build(); }
application.yaml配置
spring: security: oauth2: client: registration: github: clientId: <hidden> clientSecret: <hidden> redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
更新发现
更新: 我发现问题源于以下配置,但不清楚该如何调整:
.sessionManagement(configurer -> configurer .sessionCreationPolicy(SessionCreationPolicy.STATELESS) )
我希望应用不使用会话,仅通过Github颁发的Access Token进行通信,但目前却设置了JSESSIONID Cookie。如何确保授权成功后返回Access Token而非设置Cookie?
解决方案
问题根源
- 匿名状态:
SessionCreationPolicy.STATELESS禁用了会话,但OAuth2 Login默认依赖会话保存授权后的认证信息,导致登录后SecurityContext无法获取有效认证对象,显示匿名。 - JSESSIONID Cookie:OAuth2授权码流程中需要临时会话存储授权请求状态,即使设置无状态,仍会生成临时会话Cookie。
- Token返回:默认OAuth2 Login完成后仅跳转页面,不会主动返回Access Token给前端。
具体调整步骤
1. 重构安全配置,适配无状态授权流程
移除强制无状态配置,改用按需创建会话策略,同时自定义授权成功处理器返回Token:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, Converter<Jwt, UsernamePasswordAuthenticationToken> converter, OAuth2AuthorizedClientService authorizedClientService) throws Exception { http .authorizeHttpRequests(authorize -> authorize .anyRequest().authenticated() // 按需开放接口,此处先统一要求认证 ) .csrf(AbstractHttpConfigurer::disable) .cors(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .oauth2Login(oauth2 -> oauth2 .successHandler((request, response, authentication) -> { // 从认证对象中提取Github颁发的Access Token OAuth2AuthenticationToken oauth2Token = (OAuth2AuthenticationToken) authentication; OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient( oauth2Token.getAuthorizedClientRegistrationId(), oauth2Token.getName() ); String accessToken = authorizedClient.getAccessToken().getTokenValue(); // 将Token以JSON格式返回给前端 response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getWriter(), Map.of("access_token", accessToken)); }) ) .oauth2ResourceServer(configurer -> configurer .jwt(jwt -> jwt.jwtAuthenticationConverter(converter)) ) .sessionManagement(configurer -> configurer .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 仅在必要时创建会话 .sessionFixation().none() // 禁用会话固定保护,避免额外Cookie ) .exceptionHandling(configurer -> configurer .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) .accessDeniedHandler(new BearerTokenAccessDeniedHandler()) ); return http.build(); }
2. 完善Github OAuth2客户端配置
添加必要的权限范围和提供商信息,确保正确获取Token:
spring: security: oauth2: client: registration: github: clientId: <hidden> clientSecret: <hidden> redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}' scope: read:user,user:email # 根据业务需求添加Github权限 provider: github: authorization-uri: https://github.com/login/oauth/authorize token-uri: https://github.com/login/oauth/access_token user-info-uri: https://api.github.com/user user-name-attribute: login
3. 禁用不必要的会话Cookie
通过Servlet上下文配置,避免生成默认的JSESSIONID:
@Bean public ServletContextInitializer servletContextInitializer() { return servletContext -> { SessionCookieConfig cookieConfig = servletContext.getSessionCookieConfig(); cookieConfig.setMaxAge(0); // 会话Cookie立即过期 cookieConfig.setHttpOnly(true); // 增强安全性 cookieConfig.setSecure(true); // 生产环境建议开启,仅HTTPS传输 }; }
4. 调整接口测试逻辑
修改HomeController,确保仅接受携带有效Bearer Token的请求:
@RestController @RequestMapping(path = "/") public class HomeController { @GetMapping public String home(Authentication authentication) { return "Hello, %s".formatted(authentication.getName()); } }
关键说明
- OAuth2授权码流程天然需要临时会话存储授权状态,完全无状态需自行实现状态存储(如Redis),简单场景下
IF_REQUIRED策略足够满足需求。 - 自定义
successHandler是实现Token返回的核心,替换默认的跳转逻辑,直接向前端返回Access Token。 - 后续前端请求需在请求头中携带
Authorization: Bearer {access_token},资源服务器会自动解析并完成认证。
内容的提问来源于stack exchange,提问作者ulxanxv
相关产品推荐
相关产品推荐

