使用github OAuth實現用戶登錄
做登錄功能時,允許使用第三方網站的身份,這稱為"第三方登錄"。
原理
github內的認證方法
在github上申請OAuth App,進入個人的Github首頁,Settings->Applications->Developer applications->Register a new application
注冊這個app的目的:You can enable other users to authorize your OAuth App.
您可以讓其他用戶授權您的OAuth應用程序。
獲取code和state
<a href="https://github.com/login/oauth/authorize?client_id=1d1f99e3efc33edcbf45&redirect_uri=http://localhost:8080/callback&scope=user&state=1">登陸</a>
scope
屬性列出了用戶授予的附加到令牌的范圍。通常,這些范圍將與您要求的范圍相同。但是,用戶可以編輯其范圍,從而有效地授予您的應用程序比您最初請求的訪問權限少的權限。此外,用戶可以在OAuth流完成后編輯令牌作用域。您應該意識到這種可能性,並相應地調整應用程序的行為。
處理用戶選擇授予您的訪問權限比您最初請求的權限少的錯誤情況非常重要。例如,應用程序可以警告其用戶或以其他方式與其用戶進行通信,告知他們功能將減少或無法執行某些操作。
此外,應用程序始終可以再次將用戶發送回流程,以獲取其他權限,但是請不要忘記用戶總是可以拒絕。
client_id就是注冊給的那個,redirect_uri就是你寫的回調地址,通過訪問這個地址獲取令牌
Name | Type | Description |
---|---|---|
client_id |
string |
Required. The client ID you received from GitHub when you registered 必需的。注冊時從GitHub收到的客戶端ID。 |
redirect_uri |
string |
The URL in your application where users will be sent after authorization. See details below about redirect urls應用程序中的URL,用戶將在授權后發送到該URL。請參閱下面關於重定向url的詳細信息。 |
login |
string |
Suggests a specific account to use for signing in and authorizing the app.建議使用一個特定的帳戶來登錄和授權應用程序。 |
scope |
`string | 以空格分隔的范圍列表。如果沒有提供作用域,對於沒有為應用程序授權任何作用域的用戶,作用域默認為空列表。對於已經為應用程序授權了范圍的用戶,不會顯示帶有范圍列表的OAuth授權頁面。相反,流的這一步將使用用戶為應用程序授權的范圍集自動完成。例如,如果用戶已經執行了兩次web流,並且授權了一個具有用戶范圍的令牌和另一個具有回購范圍的令牌,則第三個web流不提供sc |
state |
string |
An unguessable random string. It is used to protect against cross-site request forgery attacks.不可猜測的隨機字符串。它用於防止跨站點請求偽造攻擊。 |
allow_signup |
string |
Whether or not unauthenticated users will be offered an option to sign up for GitHub during the OAuth flow. The default is true . Use false in the case that a policy prohibits signups.在OAuth流期間,是否會為未經身份驗證的用戶提供一個注冊GitHub的選項。默認值為true。在策略禁止注冊的情況下使用false。 |
通過獲取的code和state,利用httpclient來獲取AccessToken
java代碼callback接口如下:
@Value("${github.client.id}") private String id; @Value("${github.client.secret}") private String secret; @Value("${github.redirect.uri}") private String uri; @RequestMapping("/callback") public String callback(@RequestParam(name = "code") String code, @RequestParam(name = "state") String state, HttpServletResponse response) { //通過一個DTO對象封裝,接收的code state AccessTokenDTO accessTokenDTO = new AccessTokenDTO(); accessTokenDTO.setCode(code); accessTokenDTO.setClient_id(id); accessTokenDTO.setClient_secret(secret); accessTokenDTO.setState(state); accessTokenDTO.setRedirect_uri(uri); //得到github傳回來的數據 code + Client Secret+Client id來獲取token String accessToken = githubProvider.getAccessToken(accessTokenDTO); //通過解析封裝成一個對象 GithubUser githubUser = githubProvider.getUser(accessToken); if (githubUser != null) { //寫入數據庫 } //login success // request.getSession().setAttribute("user",githubUser); return "redirect:/"; } else { return "redirect:/"; }
利用獲取過來的AccessToken來再次訪問github來獲取用戶信息
其中githubprovider代碼如下:
public String getAccessToken(AccessTokenDTO accessTokenDTO) { //設置從GitHub那里獲取的數據設置成josn格式 MediaType mediaType = MediaType.get("application/json; charset=utf-8"); OkHttpClient client = new OkHttpClient(); //通過okhttclient來獲取github傳過來的用戶信息,要利用到AceessTokenDTO內的數據! //將傳過來的accesstokendto轉化為json格式,通過post形式傳給github 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(); //把傳過來的token解析 String token = string.split("&")[0].split("=")[1]; return token; } catch (Exception e) { //log.error("getAccessToken error,{}", accessTokenDTO, e); } return null; } public GithubUser getUser(String accessToken) { //通過得到的token獲取user 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(); //封裝 GithubUser githubUser = JSON.parseObject(string, GithubUser.class); //System.out.println(string); return githubUser; } catch (Exception e) { // log.error("getUser error,{}", accessToken, e); } return null; }
獲取的json內容如下:
//封裝 JSON.parseObject(string, GithubUser.class); { "login": "Diamondtest", "id": 28478049, "avatar_url": "https://avatars0.githubusercontent.com/u/28478049?v=3", "gravatar_id": "", "url": "https://api.github.com/users/Diamondtest", "html_url": "https://github.com/Diamondtest", "followers_url": "https://api.github.com/users/Diamondtest/followers", "following_url": "https://api.github.com/users/Diamondtest/following{/other_user}", "gists_url": "https://api.github.com/users/Diamondtest/gists{/gist_id}", "starred_url": "https://api.github.com/users/Diamondtest/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/Diamondtest/subscriptions", "organizations_url": "https://api.github.com/users/Diamondtest/orgs", "repos_url": "https://api.github.com/users/Diamondtest/repos", "events_url": "https://api.github.com/users/Diamondtest/events{/privacy}", "received_events_url": "https://api.github.com/users/Diamondtest/received_events", "type": "User", "site_admin": false, "name": null, "company": null, "blog": "", "location": null, "email": null, "hireable": null, "bio": null, "public_repos": 0, "public_gists": 0, "followers": 0, "following": 0, "created_at": "2017-05-06T08:08:09Z", "updated_at": "2017-05-06T08:16:22Z" }
授權之后,每次登陸都不需要再次授權,可更換瀏覽器或者選擇revoke all user tokens 就可以了!
okhttpclient的用例與介紹
介紹
OkHttp是一個HTTP客戶機,默認情況下是高效的:
-
http/2支持允許對同一主機的所有請求共享套接字。
-
連接池可以減少請求延遲(如果HTTP/2不可用)。
-
透明GZIP縮小下載大小。
-
響應緩存完全避免了重復請求的網絡。
OkHttpClient client = new OkHttpClient(); //獲取URL //訪問URL並獲取該網站返回過來的字符串 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(); } } public static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); OkHttpClient client = new OkHttpClient(); //發送到服務器 //通過build一個request 並將需要的參數封裝到body 訪問url,執行client.newCall(request).execute(),得到返回值,一般是一個json格式的字符串,當然也可以自己選擇它的返回數據格式! 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(); } }
詳情參見:okhttp