Github OAuth 认证登录 1. Introduction 在完成项目时,可能会使用到第三方登录的情况,例如:WeChat、QQ,而Github OAuth 也是其中的一种。
Github OAuth 是一种基于授权码的认证模式,由github服务器完成授权码的认证和用户信息的传输。其登录模式大致可列为以下几个步骤
用户点击登录,网站 A 让用户跳转到 GitHub。
GitHub 要求用户登录,然后询问”A 网站要求获得 xx 权限,你是否同意?”
用户同意,GitHub 就会重定向回 A 网站,同时发回一个授权码。
A 网站使用授权码,向 GitHub 请求令牌。
GitHub 返回令牌.
A 网站使用令牌,向 GitHub 请求用户数据。
本文旨在介绍Github OAuth的一种简单用法,无需自己搭建服务器,简单调用API,用于自己的项目之中。
2. 前置准备 在正式在项目中使用OAuth API 前,我们需要先创建一个OAuth Application,访问网址 https://github.com/settings/applications/new
在开发环境中,可如下图创建:
其中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
对应 用户授权范围
state
是一个随意值,用来防止跨站请求伪造攻击。
redirect_uri
是你授权后跳转的网页路径。
4. 用户携带code,去获得令牌 用户携带code
和state
向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" ) .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
获取对应的信息。
此处同样使用 okHttp
和 fastJson
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") 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); String token = githubProvider.accessTokenProvider(accessToken); GithubUser githubUser = githubProvider.userProvider(token); if (githubUser!=null ){ 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); response.addCookie(new Cookie ("token" ,Token)); return "redirect:/" ; } else { return "redirect:/" ; } }
7、提升 – TODO 由于Github服务器处于外国,在国内访问,有时十分缓慢,即使使用了科学上网的方式,在用户信息传输时,会抛出 SocketTimeOutException
。
看了其他的博客,解决方法是
在项目中配置全局代理 proxy
,使访问时的速度变快
增加 Socket
连接和传输超时的时间。
此处有待实验和解决。