Spring Boot中如何使用并验证GitHub OAuth 2.0 Token?
前提条件
拥有Spring Boot应用、GitHub Client ID、GitHub Client Secret,已配置如下SecurityConfiguration类:
@AllArgsConstructor @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class SecurityConfiguration { private final AuthenticationExceptionHandler authenticationExceptionHandler; private final AccessDeniedExceptionHandler accessDeniedExceptionHandler; private final OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService; @Bean protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .headers().frameOptions().sameOrigin() .and() .authorizeRequests(a -> a .antMatchers("/", "/login/**", "/error", "/webjars/**").permitAll() .anyRequest().authenticated() ) .exceptionHandling(e -> e .authenticationEntryPoint(authenticationExceptionHandler) .accessDeniedHandler(accessDeniedExceptionHandler) ) .csrf(c -> c .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ) .logout(l -> l .logoutSuccessUrl("/").permitAll() ) .oauth2Login(); return http.build(); } }
问题描述
使用Postman发送GET请求http://{server:port}/oauth2/authorization/github获取到GitHub Token后,在请求http://{server:port}/api/v1/users时添加请求头Authorization: Bearer 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,却收到401未授权错误。
问题解答
1. 后续请求如何正确使用该GitHub Token?
当前配置的是OAuth2授权码模式,你拿到的GitHub Token是用于访问GitHub API的凭证,并非给你的Spring Boot应用做认证用的。在授权码模式下,用户完成授权后,Spring Boot应用会生成自己的会话Cookie,后续请求需要携带这个Cookie才能通过认证。
如果要实现直接用GitHub Token调用你的API,需要切换为OAuth2资源服务器模式,让应用直接验证GitHub Token的有效性并进行授权。
2. Spring Boot中何处可验证该Token有效性?
- 自定义服务类,调用GitHub的
GET /user接口(请求时带上待验证的Token),若返回200状态码则说明Token有效; - 借助Spring Security OAuth2资源服务器的自动验证机制,配置后框架会帮你完成Token的校验逻辑。
3. 如何在过滤器链中实现GitHub Token校验?
步骤1:添加资源服务器依赖
如果使用Maven,在pom.xml中添加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
步骤2:修改SecurityFilterChain配置
GitHub的access token是不透明令牌(Opaque Token),而非JWT,因此需要配置不透明令牌的校验逻辑:
@AllArgsConstructor @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class SecurityConfiguration { private final AuthenticationExceptionHandler authenticationExceptionHandler; private final AccessDeniedExceptionHandler accessDeniedExceptionHandler; private final OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService; @Bean protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .headers().frameOptions().sameOrigin() .and() .authorizeRequests(a -> a .antMatchers("/", "/login/**", "/error", "/webjars/**").permitAll() .anyRequest().authenticated() ) .exceptionHandling(e -> e .authenticationEntryPoint(authenticationExceptionHandler) .accessDeniedHandler(accessDeniedExceptionHandler) ) .csrf(c -> c .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ) .logout(l -> l .logoutSuccessUrl("/").permitAll() ) .oauth2Login() // 保留原有的授权码登录流程 .and() .oauth2ResourceServer(oauth2 -> oauth2 .opaqueToken(token -> token .introspector(opaqueTokenIntrospector()) // 配置自定义令牌校验器 ) ); return http.build(); } // 自定义不透明令牌校验器 @Bean public OpaqueTokenIntrospector opaqueTokenIntrospector() { return token -> { // 调用GitHub的/user接口验证Token有效性 RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(token); HttpEntity<Void> entity = new HttpEntity<>(headers); ResponseEntity<Map> response = restTemplate.exchange( "https://api.github.com/user", HttpMethod.GET, entity, Map.class ); if (response.getStatusCode().is2xxSuccessful()) { Map<String, Object> userInfo = response.getBody(); // 生成用户权限,可根据实际需求自定义 List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); return OAuth2AuthenticatedPrincipal.create(userInfo, authorities); } else { throw new OAuth2AuthenticationException("无效的GitHub Token"); } }; } }
说明
配置完成后,当请求携带Authorization: Bearer {GitHub-Token}头时,Spring Security会调用opaqueTokenIntrospector中的逻辑验证Token有效性:
- 若验证通过,会生成对应的认证信息,允许请求访问受保护资源;
- 若验证失败(比如Token过期、无效),则返回401未授权错误。
内容的提问来源于stack exchange,提问作者West Side

