OAuth
(全称:Open Authorization
)是目前最流行的授权机制,用来授权第三方应用,获取用户数据。先前登录任何一个网站必须要注册,而繁琐的信息填写让用户很是无奈,现在可以通过OAuth
协议来授权登录第三方应用,免去了注册流程,一定程度上提高了用户留存率。
授权码方式授权大体上分为四步:
下面是我画的一个简易时序图:
可以看到用户只要一次授权即可完成登录,而授权码、令牌等交给应用服务器去处理,非常方便!为什么授权服务器要返回code
而不是直接返回access_token
,这是一步冗余操作吗?
首先它不是冗余操作,这和它的请求方式是有关系的,是为了保证安全,在后边实战环节是可以看到的。
get
请求,在授权服务器拿到用户授权后,是通过超链接中redirect_uri
传回来的,在浏览器地址栏就可以看到,如果直接返回access_token
的话,是很不安全的。OAuth
引入授权码,授权码可见无所谓,因为去请求令牌时还会再次校验(更高层次的校验,请求时不光要授权码还要应用本身的的一些身份信息),但是这次授权服务器响应没经过浏览器,直接到达后端,这样虽然多了一次请求但是保证了令牌的安全。写在前面,我对接了
GitHub
、微博登录,流程基本一致,但是在一些API参数上有出入,需要自己去查询文档,如OpenID
,微博要uid
等等。GitHub
是最简单的,可以localhost
本地测试,像微博、IP
和域名了,微信貌似不支持个人开发。
以下以GitHub
为例开始实战环节:
GitHub
上登记应用信息,获取client id
和client secret
<a id="github" class="github" href="https://github.com/login/oauth/authorize?client_id=xx&redirect_uri=http://127.0.0.1:8085/githubcallback&scope=user&state=1"> <i class="fa fa-github"></i> </a> 复制代码
code
,并携带code
申请令牌// 封装申请令牌的参数 @Data public class GitHubAccessTokenDTO { // 注册时收到的客户端ID private String client_id; // 注册时收到的客户密码 private String client_secret; // 授权码 private String code; // 重定向uri 授权后让用户跳转到哪里 private String redirect_uri; // 自己提供的随机字符串 防止跨站攻击 private String state; } // 发送Post请求,申请令牌(使用的是okhttp3) public String getAccessToken(GitHubAccessTokenDTO accessTokenDTO) { MediaType mediaType = MediaType.get("application/json; charset=utf-8"); OkHttpClient client = new OkHttpClient(); RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(accessTokenDTO)); Request request = new Request.Builder() .url("https://github.com/login/oauth/access_token") .post(body) .build(); try (Response response = client.newCall(request).execute()) { String str = response.body().string(); // 截取有用的部分 String token = str.split("&")[0].split("=")[1]; return token; } catch (IOException e) { log.error("get GitHub access_token error, {}", e); } return null; } // Controller接收code,state,请求令牌 @GetMapping("/githubcallback") public String github_callback(@RequestParam(name = "code") String code, @RequestParam(name = "state") String state) { GitHubAccessTokenDTO accessTokenDTO = new GitHubAccessTokenDTO(); accessTokenDTO.setClient_id(github_clientId); accessTokenDTO.setClient_secret(github_secret); accessTokenDTO.setState(state); accessTokenDTO.setCode(code); accessTokenDTO.setRedirect_uri(github_redirectUri); String accessToken = gitHubProvider.getAccessToken(accessTokenDTO); } 复制代码
access_token
请求用户信息public GitHubUser getUser(String accessToken) { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://api.github.com/user") .header("Authorization", "token " + accessToken) .build(); try { Response response = client.newCall(request).execute(); String str = response.body().string();// json格式,需转换(使用的是FastJSON) GitHubUser gitHubUser = JSON.parseObject(str, GitHubUser.class); return gitHubUser; } catch (IOException e) { log.error("get GitHub User error, {}", e); } return null; } // 在github_callback()接着请求 GitHubUser gitHubUser = gitHubProvider.getUser(accessToken); 复制代码
// 在github_callback()中处理 if (gitHubUser != null) {// 登录成功 // 数据库user拿想要的信息(也可以登录态持久化,添加Cookie等一系列操作。。。) // userService.insert(user);。。。 // 登录成功,回到首页 return "redirect:/"; } 复制代码
在对接其他授权服务的过程中,需要查阅相关文档,有的文档描述不清晰的,还需要手动Debug
去测试,还是挺锻炼一个人能力的,当然全部对接完成后还是有一定成就感的!
那以上就是我的一些个人见解,如有误,还请大家指正!!!