本文章案例为Java在web端接入GitHub登录,大致流程相同。
一: 设置Github的ClientId和
1.1 创建一个newOAuth app
1.2 创建成功之后会生成一个Clinet ID和 Client secrets 用于在服务端交互时候使用
二:客户端按钮跳转代码
按钮的跳转地址为:
https://github.com/login/oauth/authorize?client_id=KaTeX parse error: Expected 'EOF', got '&' at position 16: {你生成的client_id}&̲redirect_uri={你服务端的跳转地址}.
假如我的client_id为 Ov22342345234,服务端的回调地址:http://127.0.0.1:8080/admin/github/callback
则我客户端的地址为:https://github.com/login/oauth/authorize?client_id=Ov22342345234\&redirect_uri=http://127.0.0.1:8080/admin/github/callback
然后嵌入到web页面中。
html
<div class="row">
<div class="col-xs-12">
<a href="https://github.com/login/oauth/authorize?client_id=${你生成的client_id}&redirect_uri=http://127.0.0.1:8080/admin/github/callback" class="btn btn-default btn-block btn-flat">
使用 GitHub 登录
</a>
</div>
</div>
三:服务端回调地址:
用户在客户端点击GitHub登录点击授权登录,GitHub会回调到redirect_uri的地址,在后面会带一个code码,我们服务端回拿着这个code码换取access_token,因为要校验client_id下服务的唯一性
3.1 表设计:
如果B2C业务模式的话:C端用户player,B端用户 user
如果B2B2C业务模式的话: C端用户player,B端用户 user,多一个M端用户manager(用来控制平台的运转比如数据修复等等相关业务)
more字段是存储三方回调成功之后的用户信息,比如:{"login":"xxx","id":xxx,"avatar_url":"xxx","name":"xxx","email":"xxxx@email.com"}
sql
CREATE TABLE `third_user_account` (
`third_user_account_id` bigint(20) unsigned NOT NULL COMMENT '主键ID',
`create_id` bigint(20) NOT NULL COMMENT '创建人 @link user.id (-1为:系统)',
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`modifier_id` bigint(20) unsigned DEFAULT NULL COMMENT '修改人id @link user.id (-1为:系统)',
`modify_time` datetime(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '修改时间',
`version` int(11) NOT NULL DEFAULT '1' COMMENT '版本号',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除: true 删除 false 未删除',
`enabled` tinyint(1) DEFAULT '1' COMMENT '启用状态: true 启用 (默认) false: 禁用',
`is_bound` tinyint(1) DEFAULT '0' COMMENT '是否已绑定: 0 未绑定(默认) 1 已绑定 ',
`third_plat_type_code` enum('github','qq','wechat') NOT NULL COMMENT '三方平台类型',
`third_unique` varchar(100) NOT NULL COMMENT '三方平台唯一标识(如三方ID标识)',
`third_username` varchar(155) DEFAULT NULL COMMENT '账号',
`third_email` varchar(155) DEFAULT NULL COMMENT '邮箱',
`more` text DEFAULT NULL COMMENT '其他',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`third_user_account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='三方用户账号表';
四: 编写服务端代码 (本文以Java为例)
这里的代码是GitHub
java
@RequestMapping("/github/callback")
public String handleGitHubCallback(@RequestParam("code") String code, HttpServletResponse response) {
String accessToken = getAccessTokenFromGitHub(code);
// 使用 access_token 获取用户信息
GitHubUser user = getGitHubUserInfo(accessToken);
// 绑定用户
XxlJobUser jobUser = bindGitHubUserToPlatform(user);
if (!validUserLogin(jobUser)) {
log.error("用户:{}, 账号不存在或已经禁用", jobUser);
return "账号不存在或已经禁用";
}
log.info("callback code:{}, state:{}", jobUser);
CookieUtil.set(response, LOGIN_IDENTITY_KEY, makeToken(jobUser), Boolean.TRUE);
// 重定向到用户登录后的页面
return "redirect:/";
}
- 使用 OkHttp 获取 GitHub access_token
java
private String getAccessTokenFromGitHub(String code) {
String clientId = "你生成的client_id";
String clientSecret = "你的密钥";
String url = "https://github.com/login/oauth/access_token";
String redirectUri = "http://127.0.0.1:8080/admin/github/callback";
OkHttpClient client = new OkHttpClient();
// 构建请求体
RequestBody formBody = new FormBody.Builder()
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("code", code)
.add("redirect_uri", redirectUri)
.build();
// 创建 POST 请求
Request request = new Request.Builder()
.url(url)
.post(formBody)
.header("Accept", "application/json")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
// 获取响应体并返回 access_token
String responseBody = response.body().string();
return parseAccessTokenWithGson(responseBody);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
- 使用 Gson 解析 access_token
java
private String parseAccessTokenWithGson(String responseBody) {
// 使用 Gson 解析返回的 JSON 响应
JsonObject jsonObject = JsonParser.parseString(responseBody).getAsJsonObject();
return jsonObject.get("access_token").getAsString();
}
- 根据 GitHub 用户信息绑定或创建用户账户.检查用户 email 是否已经注册,或者根据 GitHub id 进行绑定.
java
/**
* 根据 GitHub 用户信息绑定或创建用户账户.
* 检查用户 email 是否已经注册,或者根据 GitHub id 进行绑定.
* @param gitHubUser
* @return
*/
public XxlJobUser bindGitHubUserToPlatform(GitHubUser gitHubUser) {
List<ThirdUserAccountPO> list = ChainWrappers.lambdaQueryChain(thirdUserAccountDAO).eq(ThirdUserAccountPO::getThirdUnique, gitHubUser.getId())
.eq(ThirdUserAccountPO::getThirdPlatTypeCode, ThirdPlatformTypeEnum.GITHUB.getCode()).list();
ThirdUserAccountPO thirdUserAccountPO;
if (CollectionUtils.isEmpty(list)) {
thirdUserAccountPO = new ThirdUserAccountPO();
thirdUserAccountPO.setThirdUnique(String.valueOf(gitHubUser.getId()));
thirdUserAccountPO.setCreateId(-1L);
thirdUserAccountPO.setThirdPlatTypeCode(ThirdPlatformTypeEnum.GITHUB.getCode());
Optional.ofNullable(gitHubUser.getEmail()).ifPresent(v -> thirdUserAccountPO.setThirdEmail(v));
Optional.ofNullable(gitHubUser.getLogin()).ifPresent(v -> thirdUserAccountPO.setThirdUsername(v));
thirdUserAccountPO.setMore(new Gson().toJson(gitHubUser));
thirdUserAccountPO.setIsBound(Boolean.TRUE);
thirdUserAccountDAO.insert(thirdUserAccountPO);
} else {
thirdUserAccountPO = list.get(0);
}
//生成绑定到后台账户
XxlJobUser xxlJobUser = ChainWrappers.lambdaQueryChain(xxlJobUserDao)
.eq(XxlJobUser::getThirdUserAccountId, thirdUserAccountPO.getThirdUserAccountId()).one();
String thirdUnique = thirdUserAccountPO.getThirdUnique();
if (xxlJobUser == null) {
xxlJobUser = new XxlJobUser();
xxlJobUser.setUsername("gh_".concat(thirdUnique.substring(0,3))
.concat(thirdUserAccountPO.getThirdUsername()));
HashCode hashPwd = Hashing.hmacMd5(thirdUnique.getBytes(StandardCharsets.UTF_8)).hashString(thirdUnique, StandardCharsets.UTF_8);
xxlJobUser.setPassword(hashPwd.toString());
xxlJobUser.setRole(0);
xxlJobUser.setThirdUserAccountId(thirdUserAccountPO.getThirdUserAccountId());
xxlJobUserDao.save(xxlJobUser);
}
return xxlJobUser;
}
- 校验用户是否是可用状态
java
private Boolean validUserLogin(XxlJobUser xxlJobUser) {
if (xxlJobUser.getDeleted() || xxlJobUser.getStatus() != AccountStatusEnum.ENABLED.getCode()) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
总结
本文只是使用web演示了GitHub的登录接入,如果使用App跳转,只需要把接口请求成功后,返回跳转地址的标识改为跳转回APP就可以了。
参考资料 & 致谢
[1] GitHub Apps