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

基于Spring Boot实现Github OAuth的code兑换token方案问询

Spring Boot Github OAuth 登录及获取用户邮箱解决方案

一、正确获取Github授权返回的code

Github OAuth授权完成后,会自动重定向到你在Github后台配置的回调地址,并在URL的查询参数中携带code。你只需要在回调接口中直接从请求参数提取即可,示例代码如下:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.kohsuke.github.GHUser;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

@RestController
public class GithubOAuthController {

    // 回调地址必须与Github OAuth应用配置的完全一致
    @GetMapping("/github/callback")
    public String handleCallback(@RequestParam("code") String code) {
        // 拿到code后兑换access_token
        String accessToken = exchangeCodeForToken(code);
        // 用token获取用户所有邮箱
        List<String> emails = fetchUserEmails(accessToken);
        return "用户关联邮箱:" + String.join(", ", emails);
    }

    private String exchangeCodeForToken(String code) {
        RestTemplate restTemplate = new RestTemplate();
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("client_id", "你的Github应用Client ID");
        params.add("client_secret", "你的Github应用Client Secret");
        params.add("code", code);
        params.add("redirect_uri", "http://localhost:8080/github/callback"); // 与配置的回调地址完全一致

        // 发送POST请求兑换token,Github返回form格式响应
        String response = restTemplate.postForObject("https://github.com/login/oauth/access_token", params, String.class);
        
        // 解析响应提取access_token,响应格式:access_token=xxx&scope=xxx&token_type=bearer
        for (String param : response.split("&")) {
            if (param.startsWith("access_token=")) {
                return param.substring("access_token=".length());
            }
        }
        return null;
    }

    private List<String> fetchUserEmails(String accessToken) {
        try {
            GitHub github = new GitHubBuilder().withOAuthToken(accessToken).build();
            // 获取所有邮箱需要user:email权限,授权时必须指定该scope
            List<GHUser.Email> emailList = github.getMyself().getAllEmails();
            return emailList.stream().map(GHUser.Email::getEmail).collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
            return List.of();
        }
    }
}

二、常见问题排查(解决你遇到的空值问题)

  • 回调地址不匹配:Github后台配置的回调地址、兑换token时传入的redirect_uri、Spring控制器的路径三者必须完全一致(包括HTTP/HTTPS、端口、路径),否则Github不会返回code,或兑换token失败。
  • 授权Scope缺失:要获取用户邮箱,必须在引导用户授权的URL中添加scope=user:email参数,示例授权URL:
    https://github.com/login/oauth/authorize?client_id=你的Client ID&redirect_uri=http://localhost:8080/github/callback&scope=user:email
    
  • 参数解析错误:如果手动拼接请求URL,容易出现参数编码问题,建议用RestTemplate或OkHttp自动处理参数,避免手动拼接出错。

三、简化方案:用Spring Security OAuth2 Client自动处理流程

如果不想手动处理code和token的兑换,直接用Spring Security的OAuth2 Client模块,它会自动完成授权跳转、code兑换token的全流程:

  1. 引入Maven依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
  1. 配置application.yml:
spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: 你的Github应用Client ID
            client-secret: 你的Github应用Client Secret
            scope: user:email
        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
  1. 控制器中直接获取token并调用API:
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.kohsuke.github.GHUser;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

@RestController
public class GithubUserController {

    @GetMapping("/user/emails")
    public List<String> getUserEmails(@AuthenticationPrincipal OAuth2User oAuth2User) {
        // 从OAuth2User中直接获取access_token
        String accessToken = oAuth2User.getAttributes().get("access_token").toString();
        
        try {
            GitHub github = new GitHubBuilder().withOAuthToken(accessToken).build();
            List<GHUser.Email> emailList = github.getMyself().getAllEmails();
            return emailList.stream().map(GHUser.Email::getEmail).collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
            return List.of();
        }
    }
}

这种方式无需手动处理授权流程,Spring Security会自动拦截未授权请求,跳转至Github登录页面,登录完成后自动获取token,大幅减少出错概率。


内容的提问来源于stack exchange,提问作者Phill Conrad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 16:58:22