GitHub OAuth授權登錄

GitHub OAuth授權登錄

OAuth

第三方登入主要基於OAuth 2.0。OAuth(開放授權)協議是一個開放標準,爲用戶資源的授權提供了一個安全的、開放而又簡易的標準。與以往的授權方式不同之處是OAUTH的授權不會使第三方觸及到用戶的帳號信息(如用戶名與密碼),即第三方無需使用用戶的用戶名與密碼就可以申請獲得該用戶資源的授權,因此OAUTH是安全的
---- 百度百科

實現步驟

GitHub OAuth API官方文檔

Building OAuth Apps

  • 創建OAuth App
    在這裏插入圖片描述

這裏的Authorization callback URL在後文中會介紹
這裏僅做本地開發,所以填了本地地址,如果項目部署好了,應該填http://xxx/callback
打開OAuth documentation查看官方文檔

GitHub授權流程

在這裏插入圖片描述

  • 這裏介紹瞭如何使自己的項目獲取GitHub授權的流程:

第一步:跳轉至GitHub去請求認證
第二步:認證成功後,由GitHub跳轉回我自己的項目
第三步:我自己的項目通過GitHub跳轉回來時帶的access token去訪問GitHub API

Step1.

在這裏插入圖片描述

這是第一步去GitHub認證的request所需帶的參數,通過GET方式發送該請求https://github.com/login/oauth/authorize,並帶參數
註冊OAuth App後會提供一個client_id
認證成功後回跳轉到redirect_uri頁面
login參數指定一個賬戶去獲取授權
scope是授權後我們想要拿到的信息,這裏我只需要user
state參數非必需,用於防治跨域僞造請求攻擊
allow_signup就是是否向未認證的用戶提供註冊GitHub的選項,默認是允許的

####Step2
在這裏插入圖片描述

如果GitHub用戶接受該授權請求,則GitHub將帶着codestate參數重定向到自己之前註冊的那個callback地址,我這裏填的是http://localhost:8080/callback,因此授權成功後會跳轉至下面這個網址,這裏看到帶了code參數,state就是上一步請求中帶的state參數,原樣返回。這裏服務端需要接收這個codestate參數用於之後獲取access_token

http://localhost:8080/github/oauth/callback?code=14de2c737aa02037132d&state=1496989988474

拿到請求中的code參數後服務端向https://github.com/login/oauth/access_token這個API發送POST請求,並且在該請求中帶上client_id,client_secret,code參數,請求成功後會返回帶有access_token的信息。

這裏請求返回的Json格式信息是access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&token_type=bearer,而我們需要的token是e72e16c7e42f292c6912e7710c838347ae178b4a,因此在處理時要注意。

Step3.

在這裏插入圖片描述

獲取到access_token後, 再調用https://api.github.com/user?access_token=xxx這個API,就可以獲取之前scope中對應的GitHub用戶信息。 用戶的基本信息內容如下所示, 根據第一步傳入的不同的 scope,獲取到的用戶信息也是不同的,博客後臺使用 login 字段作爲用戶的唯一標示。

整個流程的順序圖

在這裏插入圖片描述
這裏再盜一張linwalker第三方登入例子-GitHub授權登入(node-koa)中的圖
在這裏插入圖片描述

實現

Step1.

點擊“登錄”,向GitHub發送授權請求

<li th:if="${session.user == null}">
    <a href="https://github.com/login/oauth/authorize?client_id=xxx&redirect_uri=http://localhost:8080/callback&scope=user&state=1">登錄</a>
</li>

Step2.

GitHub帶參數code回調callback地址
服務端拿到code參數POST請求GitHub獲取access_token
GitHub返回access_token
服務端拿到access_token請求用戶信息,之後將該用戶信息存到數據庫實現持久化

AuthorizeController處理callback頁面

@Controller
public class AuthorizeController {

    @Autowired
    private GithubProvider githubProvider;

    //在application.properties中配置github.client.id等參數
    @Value("${github.client.id}")
    private String clientId;

    @Value("${github.client.secret}")
    private String clientSecret;

    @Value("${github.redirect.uri}")
    private String redirectUri;

    @Autowired
    private UserMapper userMapper;

    @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);

        //通過accessTokenDTO獲取相應的access_token
        String accessToken = githubProvider.getAccessToken(accessTokenDTO);
        //再由獲取的access_token獲取GitHub用戶GithubUser對象
        GithubUser githubUser = githubProvider.getUser(accessToken);

        /**
         * 登陸成功,寫Cookie
         * 新建一個論壇用戶並將其信息和該GitHub用戶信息綁定起來,並將相關信息保存到數據庫
         */
        if (githubUser != null && githubUser.getId() != null) {
            User user = new User();
            String token = UUID.randomUUID().toString();
            user.setToken(token);
            user.setName(githubUser.getName());
            user.setAccountId(String.valueOf(githubUser.getId()));
            user.setGmtCreate(System.currentTimeMillis());
            user.setGmtModified(user.getGmtCreate());
            user.setAvatarUrl(githubUser.getAvatarUrl());

            //將新用戶對象插入到數據庫中
            userMapper.insert(user);
            /**
             * 並將token作爲Cookie加入到response中
             */
            response.addCookie(new Cookie("token", token));
            //重定向到首頁
            return "redirect:/";
        } else {
            // 登錄失敗,重新登錄,重定向到首頁
            return "redirect:/";
        }
    }
}

GithubProvider是用於處理相關GETPOST請求的類

@Component
public class GithubProvider {
    //getAccessToken通過參數以POST方式得到相應的access_token
    public String getAccessToken(AccessTokenDTO accessTokenDTO) {
        MediaType mediaType = MediaType.get("application/json; charset=utf-8");
        //用OkHttp模擬Http的Post、Get請求
        OkHttpClient client = new OkHttpClient();

        //用FastJson將accessTokenDTO對象轉化爲Json字符串
        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();
            //對請求返回的Json字符串進行分割從而獲取token
            String token = string.split("&")[0].split("=")[1];
            return token;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //getUser方法通過獲取的accessToken信息以GET方式去調API獲取GitHub用戶信息
    public GithubUser getUser(String accessToken) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://api.github.com/user?access_token=" + accessToken)
                .build();
        try {
            Response response = client.newCall(request).execute();
            String string = response.body().string();
            //將接受到的包含GitHub user信息的Json字符串通過FastJson轉化爲GithubUser對象
            GithubUser githubUser = JSON.parseObject(string, GithubUser.class);
            return githubUser;
        } catch (IOException e) {
        }
        return null;
    }

}

OkHttp

本項目中採用OkHttp來模擬Http的GETPOST請求

Get a URL

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}

Post to a Server

public static final MediaType JSON
    = MediaType.get("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(json, JSON);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}

在這裏插入圖片描述
在這裏插入圖片描述

這是通過獲取的accessToken信息去調https://api.github.com/user?access_token=這個API獲取GitHub用戶信息
我的論壇項目所需的信息包括id作爲用戶唯一標識,name作爲論壇暱稱,bio作爲用戶簡述以及avatarUrl作爲用戶頭像

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章