Oauth入门与Spring实现:以GitHub 第三方认证为例

发布于 2021-03-31  152 次阅读


最近在学习spring,正好接触到了Oauth这个第三方认证协议,便用Spring实现Github的Oauth认证.本文的Oauth概念部分参考了阮一峰大佬的博客.(PS. 大佬有另一篇更通俗地解释了Oauth的文章)

什么是Oauth?

下面引用了wiki中的定义.

开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

也就是说Oauth是一个第三方认证标准,用于规定第三方认证.

Oauth的流程是什么?

OAuth运行流程

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

其中,Resource Owner就是我们所说的用户;Client就是为用户提供服务的服务器;Authorization ServerResource Server 都是隶属于第三方的服务器,分别进行认证和在认证结束后,像提供服务的服务器提供数据.

GithubOauth的spring实现

其中GitHub认证API的介绍见官方文档.

step0. 注册GitHub app

根据GitHub文档提示,我们要先注册一个GitHubApp,并得到GitHubapp的id和密钥.

step1. Get请求API

GET https://github.com/login/oauth/authorize

构造GET请求,其中client_ID是必须传输的,同时应该指定跳转的url.

https://github.com/login/oauth/authorize?client_id=yourClientId&redirect_uri=

在前端的A标签设置类似于上方的url,让用户进行点击,进入授权页面.

step2.使用POST请求获得token

此使用户已经被重定向到指定页面了.这是服务器需要POST上一步GitHub返回的code,以获得token.

按照获取token所需的数据,创建DTO. Get 和 set 方法使用ide自动生成便可.

public class AccessTokenDTO {
    private String client_id;
    private String client_secret;
    private String code;
    private String redirect_uri;
    private String state;
}
@GetMapping("/callback")
public String callback(@RequestParam(name = "code") String code,
                       @RequestParam(name = "state") String state,
                       HttpServletResponse response) {
    AccessTokenDTO accessTokenDTO = new AccessTokenDTO();
    accessTokenDTO.setClient_id(clientId);
    accessTokenDTO.setClient_secret(clientSecret);
    accessTokenDTO.setCode(code);
    accessTokenDTO.setRedirect_uri(redirectUri);
    accessTokenDTO.setState(state);
    String accessToken = getAccessToken(accessTokenDTO);
//  剩余部分略...
}

在重定向路径下(此处为/callback),接收GitHub传来的code和state,并使用这些数据获得token.

其中,getAccessToken方法具体如下.使用OkHttpClient构造并发送请求,这里指定为json格式.并对json进行截断得到token.

public String getAccessToken(AccessTokenDTO 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 string = response.body().string();
        String token = string.split("&")[0].split("=")[1];
        return token;
    } catch (Exception e) {
        log.error("getAccessToken error,{}", accessTokenDTO, e);
    }
    return null;
}

step3.获得用户数据

curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com/user

在GitHub官方文档中,以curl为例说明了如何获得用户信息.

使用curl --help 查询了一下, -H就是在构造自己特殊的http head.

-H, --header Pass custom header(s) to server

因此我们再次使用okHttpClient构造http请求,并在head中加上Authorization字段.并以JSON形式进行返回.

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 string = response.body().string();
        return JSON.parseObject(string, GithubUser.class);
    } catch (Exception e) {
        log.error("getUser error,{}", accessToken, e);
    }
    return null;
}

至此,我们通过Oauth认证得到了第三方用户的资料.我们再建立一个UserDTO将其存入数据库后,我们的用户就通过GitHubAPI完成对我们网站的登录啦!


你好哇!欢迎来到雷公马碎碎念的地方:)