原文链接
前言
前面我们介绍了Web端支持Google账号登录,作为另一个常用的的第三方登录手段,这里我们再处理一下Github的授权登录功能。由于都是使用OAuth2,所以基本流程区别不大,故而我们这里就不使用专门的包了,全部手动使用HTTP请求完成
实现
登录的基本流程为:用户点击Github图标 -> 携带你的客户端ID以及相关参数跳转到Github授权页面 -> 用户同意授权 -> Github携带code跳转到你指定的回调的地址 -> 该页面检测code信息不为空等其他你自己的相关前端业务逻辑,完成后调用你的后端接口 -> 后端接口收到code综合github的客户端ID和密钥获取信息查询token -> 利用该token再次调用github相关接口获取指定用户信息 -> 完成注册/登录
应用程序申请
这里我们直接来到Github的开发者选项,选择OAuth Apps选项卡(这里如果你选择Github Apps也可以,流程上稍有区别,当我们需要更复杂的功能,如访问和管理组织中的多个仓库,或者集成一些自动化流程才会考虑选择Github App。绝大多数情况下,对于第三方登录来说,还是选择OAuth Apps更合理),新建一个OAuth App。这里填写的信息和Google差不多,按例填写,填写完成后生成我们的密钥,即可完成申请
客户端
当用户点击我们的图标时,跳转到https://github.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_CALLBACK_ADD&scope=read:user user:email,client_id即为再申请之后获得的客户端ID,重定向地址并不是必填的,如果没有填写会使用在申请中配置的,如果填写了也需要保证为申请配置中回调地址的子域名范围以及子路径:
The
redirect_uriparameter is optional. If left out, GitHub will redirect users to the callback URL configured in the OAuth app settings. If provided, the redirect URL’s host (excluding sub-domains) and port must exactly match the callback URL. The redirect URL’s path must reference a subdirectory of the callback URL.
这里我们只获取用户基本公开信息以及邮箱,如果要获得更多或者更少内容,参考官方关于scope部分的文档,或者其他请求参数文档
当用户授权完成,Github回到我们指定的回调地址,此时处理业务相关内容,完成后携带code调用服务端代码,支持客户端任务完成(本文默认你的客户端已经处理了正常登录的相关逻辑,即当服务端返回用户信息后,自动存储token以及用户数据到浏览器缓存中)
服务端
服务端获取客户端传递的code,以及自身配置的在Github申请之后获取客户端ID和密钥,调用Github接口获取token:
@Data
public class UserGithubTokenDto {
private String access_token;
private String token_type;
private String scope;
}
private UserGithubTokenDto getGithubTokenData(String code) {
UserGithubTokenDto ret;
HttpClient client = getHttpClientProxy();
String body = String.format(
"client_id=%s&client_secret=%s&redirect_uri=%s&code=%s&grant_type=authorization_code",
URLEncoder.encode(githubClientId, StandardCharsets.UTF_8),
URLEncoder.encode(githubSecret, StandardCharsets.UTF_8),
URLEncoder.encode(githubRedirectUri, StandardCharsets.UTF_8),
URLEncoder.encode(code, StandardCharsets.UTF_8)
);
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofString(body);
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
requestBuilder.uri(URI.create("https://github.com/login/oauth/access_token"))
.setHeader("Accept", "application/json")
.setHeader("Content-Type", "application/x-www-form-urlencoded")
.POST(bodyPublisher);
HttpRequest request = requestBuilder.build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
log.info("[op:getGithubTokenData] Login github response = {}", response.body());
ret = JSON.parseObject(response.body(), UserGithubTokenDto.class);
} catch (Exception ex) {
ex.printStackTrace();
throw new CaskYuiException("Github服务器解析错误");
}
return ret;
}
对于getHttpClientProxy相关内容,小伙伴可以根据需要选择性处理,我这里的代码为:
private HttpClient getHttpClientProxy() {
ProxySelector proxySelector;
if (CaskBaseConstant.TEST_ENV.equals(active)) {
proxySelector = getProxySelector(Proxy.Type.HTTP);
} else {
proxySelector = getProxySelector(Proxy.Type.SOCKS);
}
return HttpClient.newBuilder()
.proxy(proxySelector)
.build();
}
private ProxySelector getProxySelector(Proxy.Type type) {
Proxy proxy = new Proxy(type, new InetSocketAddress(proxyAddress, proxyPort));
return new ProxySelector() {
@Override
public List<Proxy> select(URI uri) {
return Collections.singletonList(proxy);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
log.error("Connection to proxy failed: {}", ioe.getMessage());
}
};
}
其中proxyAddress和proxyPort为你配置的相关地址,更多相关内容参考本站前文
获取token后我们一般需要用户的用户名和邮箱,此时需要利用token调用Github获取用户信息的接口:
// ==================== 邮箱获取 ====================
@Data
public class UserGithubMailDto {
private String email;
private Boolean primary;
private Boolean verified;
}
private String getGithubMail(String token) {
String ret = null;
HttpClient client = getHttpClientProxy();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/user/emails"))
.setHeader("Authorization", String.format("Bearer %s", token))
.GET().build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
log.info("[op:getGithubMail] Git hub request mail = {}", response.body());
List<UserGithubMailDto> data = JSON.parseArray(response.body(), UserGithubMailDto.class);
if (!ObjectUtils.isEmpty(data)) {
boolean stdMailCheck = false;
for (UserGithubMailDto mail : data) {
if (mail.getPrimary() && mail.getVerified()) {
stdMailCheck = true;
ret = mail.getEmail();
}
}
if (!stdMailCheck) {
ret = data.get(0).getEmail();
}
}
} catch (Exception ex) {
ex.printStackTrace();
throw new CaskYuiException("无法获取Github邮箱");
}
return ret;
}
// ==================== 用户基本信息 ====================
@Data
public class UserGithubUserDto {
private String login;
//...
}
private UserGithubUserDto getGithubUser(String token) {
UserGithubUserDto ret = null;
HttpClient client = getHttpClientProxy();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/user"))
.setHeader("Authorization", String.format("Bearer %s", token))
.GET().build();
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
log.info("[op:getGithubName] Git hub request name = {}", response.body());
ret = JSON.parseObject(response.body(), UserGithubUserDto.class);
} catch (Exception ex) {
ex.printStackTrace();
throw new CaskYuiException("无法获取Github用户信息");
}
return ret;
}
之后就将用户信息填入填入业务逻辑,正常走登录/注册流程了

3923

被折叠的 条评论
为什么被折叠?



