Github OAuth 认证登录

1. Introduction

在完成项目时,可能会使用到第三方登录的情况,例如:WeChat、QQ,而Github OAuth 也是其中的一种。

Github OAuth 是一种基于授权码的认证模式,由github服务器完成授权码的认证和用户信息的传输。其登录模式大致可列为以下几个步骤

  1. 用户点击登录,网站 A 让用户跳转到 GitHub。
  2. GitHub 要求用户登录,然后询问”A 网站要求获得 xx 权限,你是否同意?”
  3. 用户同意,GitHub 就会重定向回 A 网站,同时发回一个授权码。
  4. A 网站使用授权码,向 GitHub 请求令牌。
  5. GitHub 返回令牌.
  6. A 网站使用令牌,向 GitHub 请求用户数据。

本文旨在介绍Github OAuth的一种简单用法,无需自己搭建服务器,简单调用API,用于自己的项目之中。

2. 前置准备

在正式在项目中使用OAuth API 前,我们需要先创建一个OAuth Application,访问网址 https://github.com/settings/applications/new
application

在开发环境中,可如下图创建:
create

其中Home page URL,根据具体需求创建,Authorization callback URL 根据不同的环境进行修改。

创建完毕后,我们可以获得两个独特参数,一个是client_id,一个是client_secret

3. 前端设置认证路径

在 登录 按钮处设置一个跳转,跳转至github规定的认证网址,携带你的client_id,以及一些对应的参数。通过此处跳转访问,用户可以获得一个授权码(code)。

1
<a href="https://github.com/login/oauth/authorize?client_id=(你的client_id)&redirect_uri=http://localhost:8080/callback&scope=user&state=1">登录</a>

其中,scope 对应 用户授权范围

  • user – 用户信息
  • repo – 用户仓库

state 是一个随意值,用来防止跨站请求伪造攻击。

redirect_uri 是你授权后跳转的网页路径。

4. 用户携带code,去获得令牌

用户携带codestate向github服务器发送一个post请求,获得返回的令牌 access_token

这里使用okHttp 实现 POST 和 GET 请求的发送,使用 fastJson 处理 json 数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public String accessTokenProvider(AccessToken accessToken){
MediaType mediaType = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();

RequestBody body = RequestBody.create(mediaType,JSON.toJSONString(accessToken));
Request request = new Request.Builder()
.url("https://github.com/login/oauth/access_token") //访问获取access_token的路径
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
String string = response.body().string();
String token = string.split("&")[0].split("=")[1];
System.out.println(token);
return token;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

这里的AccessToken是笔者封装的类,包含此次 POST 请求所需的参数。

1
2
3
4
5
6
7
8
@Data
public class AccessToken {
public String client_id;
public String client_secret;
public String state;
public String redirect_uri;
public String code;
}

这里的参数可以设置为全局的常量,也可写进配置文件。

1
2
3
4
github.client.id=你的client_id
github.client.secret=你的client_secret
<!-- 这里的uri是你获得accesstoken后返回的地址 -->
github.redirect.uri=http://localhost:8080/callback

5. 用户携带令牌,获取用户信息

用户得到 access_token 后,为了获取用户信息,需要携带access_token,向 github 服务器发送 GET 请求,从而根据之前设定的 scope 获取对应的信息。

此处同样使用 okHttpfastJson

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public GithubUser userProvider(String accessToken){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.github.com/user")
.header("Authorization", "Bearer " + accessToken)
.build();

try (Response response = client.newCall(request).execute()) {
// 获得用户信息
String string = response.body().string();
//对用户信息进行处理,封装成类
GithubUser githubUser = JSON.parseObject(string, GithubUser.class);
System.out.println(githubUser);
return githubUser;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

6. Controller 层控制认证

使用 SpringMVC ,在 controller 实现对第三方登录的控制,以及持久化登录的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 @GetMapping("/callback") // 设置路径和前文获取accesstoken的uri对应
public String callback(@RequestParam(name="code") String code,
@RequestParam(name="state") String state,
HttpServletRequest request,
HttpServletResponse response){
AccessToken accessToken = new AccessToken();
accessToken.setCode(code);
accessToken.setClient_id(clientId);
accessToken.setClient_secret(clientSecret);
accessToken.setState(state);
accessToken.setRedirect_uri(redirectUri);
// 获取accessToken
String token = githubProvider.accessTokenProvider(accessToken);
// 获取用户信息
GithubUser githubUser = githubProvider.userProvider(token);
if(githubUser!=null){
//登录成功,插入数据库并设置cookie
User user = new User();
user.setName(githubUser.getName());
user.setBio(githubUser.getBio());
String Token = UUID.randomUUID().toString();
user.setToken(Token);
user.setAccountId(githubUser.getId());
user.setGmtCreate(System.currentTimeMillis());
user.setGmtModified(user.getGmtCreate());
userMapper.add(user);
//设置cookie,以实现持久化登录
response.addCookie(new Cookie("token",Token));
return "redirect:/";
}
else{
return "redirect:/";
}
}

7、提升 – TODO

由于Github服务器处于外国,在国内访问,有时十分缓慢,即使使用了科学上网的方式,在用户信息传输时,会抛出 SocketTimeOutException

看了其他的博客,解决方法是

  • 在项目中配置全局代理 proxy ,使访问时的速度变快
  • 增加 Socket 连接和传输超时的时间。

此处有待实验和解决。