基于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的全流程:
- 引入Maven依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency>
- 配置
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
- 控制器中直接获取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
相关产品推荐
相关产品推荐

