You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot集成Github OAuth2登录后用户匿名,如何实现无会话Token交互

问题:Spring Boot集成Github OAuth2授权后用户仍为匿名状态,且需实现无会话、返回Access Token而非Cookie

我在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?


解决方案

问题根源

  1. 匿名状态:SessionCreationPolicy.STATELESS禁用了会话,但OAuth2 Login默认依赖会话保存授权后的认证信息,导致登录后SecurityContext无法获取有效认证对象,显示匿名。
  2. JSESSIONID Cookie:OAuth2授权码流程中需要临时会话存储授权请求状态,即使设置无状态,仍会生成临时会话Cookie。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 15:48:12