OAuth2所涉及到的對象主要有如下四個:html
OAuth2的基本流程爲:java
進入github中的Settings/Developer settings中建立一個應用,表示你的應用會使用github受權。git
填寫好相關的信息後,填寫Authorization callback URL
爲http://localhost:8080/oauth/github/callback(後面受權會用到),能夠獲得Client ID 和 Client Secret,結果以下:github
根據 GitHub 登陸連接能夠回調得到 codeweb
根據Client ID 、Client Secret 和 code 可得到 tokenspring
根據 token 得到用戶信息sql
必要的URL:數據庫
登陸頁面受權URL:apache
https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&state=%sjson
得到Token的URL:
https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&state=%s
得到用戶信息的URL:
https://api.github.com/user?access_token=%s
應用得到用戶的信息時,會返回一個惟一的標識,用於惟一標識資源全部者即用戶,因而咱們能夠將此標識與數據庫中咱們本身的本地用戶相關聯。
在進行編碼以前,咱們首先訪問上面的幾種URL,並分析流程及返回結果。
首先訪問https://github.com/login/oauth/authorize?client_id=50d7f61132da7f8574a1&redirect_uri=http://localhost:8080/oauth/github/callback&state=thisisrandomstring
分析:該URL爲引導用戶對應用受權github信息,參數client_id爲該應用建立時的Client ID,redirect_uri爲該應用建立時填寫的Authorization callback URL,state爲隨機字符串,它用於防止跨站點請求僞造攻擊。訪問時結果以下:
響應結果可以理解,而後點擊受權按鈕,就會自動跳轉到http://localhost:8080/oauth/github/callback?code=107b7d2f85201535880c&state=thisisrandomstring
,URL爲咱們填寫的回調URL,code參數即爲憑證,
state爲上一步的隨機字符串。
接下來,咱們應該獲取token,根據github官方文檔,咱們須要發起一個POST
請求,URL爲https://github.com/login/oauth/access_token
,
須要攜帶的參數以下:
Name | Type | Description |
---|---|---|
client_id |
string |
Required. The client ID you received from GitHub for your GitHub App. |
client_secret |
string |
Required. The client secret you received from GitHub for your GitHub App. |
code |
string |
Required. The code you received as a response to Step 1. |
redirect_uri |
string |
The URL in your application where users are sent after authorization. |
state |
string |
The unguessable random string you provided in Step 1. |
接下來,咱們經過Postman模擬這一個過程,結果以下:
您還能夠根據Accept標頭接收不一樣格式的內容:
Accept: application/json {"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo,gist", "token_type":"bearer"} Accept: application/xml <OAuth> <token_type>bearer</token_type> <scope>repo,gist</scope> <access_token>e72e16c7e42f292c6912e7710c838347ae178b4a</access_token> </OAuth>
嗯,成功獲取到了Token無誤,接下來該獲取用戶的信息了。發起GET請求,URL爲https://api.github.com/user
,攜帶參數access_token=獲取到的token,結果以下,能夠獲取到用戶的基本信息。
首先須要一個service用來定義oauth的一些方法,如獲取token,獲取用戶信息等。
package com.yunche.novels.service; import com.yunche.novels.vo.AuthUserVO; import org.springframework.util.MultiValueMap; /** * @author yunche * @date 2019/04/04 */ public interface AuthService { String getToken(MultiValueMap<String, String> params); AuthUserVO getUserInfo(String token); boolean checkIsExistsOpenId(String openId); boolean storeOpenIdByUser(String openId, Integer userId); String getUserNameByOpenId(String openId); }
接着,使用GitHub來完成具體的service的實現。
package com.yunche.novels.service.impl; import com.yunche.novels.mapper.AuthForGitHubMapper; import com.yunche.novels.service.AuthService; import com.yunche.novels.util.AuthHelper; import com.yunche.novels.vo.AuthTokenVO; import com.yunche.novels.vo.AuthUserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.sql.Timestamp; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * @author yunche * @date 2019/04/04 */ @Service public class GitHubAuthServiceImpl implements AuthService { @Autowired private AuthForGitHubMapper gitHubMapper; private static final String GET_TOKEN_URL = "https://github.com/login/oauth/access_token"; private static final String GET_USER_URL = "https://api.github.com/user"; private static final String CLIENT_ID = "50d7f61132da7f8574a1"; private static final String CLIENT_SECRET = "6779d154cfc44115e1f3607c0000085c5c1cf178"; private static final String REDIRECT_URI = "http://localhost:8080/oauth/github/callback"; @Override public String getToken(MultiValueMap<String, String> params) { params.add("client_id", CLIENT_ID); params.add("client_secret", CLIENT_SECRET); params.add("redirect_uri", REDIRECT_URI); AuthTokenVO authTokenVO = AuthHelper.sendPostGetToken(GET_TOKEN_URL, params); String token = authTokenVO.getAccess_token(); return token; } @Override public AuthUserVO getUserInfo(String token) { Map<String, String> map = new HashMap<>(); map.put("access_token", token); return AuthHelper.sendGetToUser(GET_USER_URL, map); } @Override public boolean checkIsExistsOpenId(String openId) { return gitHubMapper.checkIsExists(openId) > 0; } @Override public boolean storeOpenIdByUser(String openId, Integer userId) { Date date = new Date(); Timestamp timeStamp = new Timestamp(date.getTime()); return gitHubMapper.storeOpenIdByUser(openId, userId, timeStamp) > 0; } @Override public String getUserNameByOpenId(String openId) { return gitHubMapper.getUserNameByOpenId(openId); } }
將須要獲取的token和用戶信息的json封裝成對象。
package com.yunche.novels.vo; /** * @author yunche * @date 2019/04/04 */ public class AuthTokenVO { private String access_token; private String token_type; private String scope; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public String getToken_type() { return token_type; } public void setToken_type(String token_type) { this.token_type = token_type; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public AuthTokenVO() { } public AuthTokenVO(String access_token, String token_type, String scope) { this.access_token = access_token; this.token_type = token_type; this.scope = scope; } }
package com.yunche.novels.vo; /** * @author yunche * @date 2019/04/04 */ public class AuthUserVO { /** * 用戶第三方應用名 */ private String login; /** * 用戶第三方惟一標識 */ private String id; /** * 用戶第三方頭像 */ private String avatar_url; public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getAvatar_url() { return avatar_url; } public void setAvatar_url(String avatar_url) { this.avatar_url = avatar_url; } }
mapper類操做數據庫。
package com.yunche.novels.mapper; import org.apache.ibatis.annotations.*; import java.util.Date; /** * @author yunche * @date 2019/04/05 */ @Mapper public interface AuthForGitHubMapper { /** * 檢查該openId是否已經註冊過 * @param openId * @return */ @Select("SELECT COUNT(*) FROM oauth_detail WHERE open_id=#{openId} and app_type='github'") Integer checkIsExists(String openId); /** * 存儲該OpenId * @param openId * @param userId * @return */ @Insert("INSERT INTO oauth_detail(open_id, app_type, user_id, status, create_time) VALUES(#{openId},'github',#{userId},1,#{createTime})") Integer storeOpenIdByUser(@Param(value = "openId") String openId, @Param(value = "userId") Integer userId, @Param(value = "createTime") Date createTime); @Select("SELECT user_name FROM user, oauth_detail WHERE user_id=user.id AND open_id = #{openId}") String getUserNameByOpenId(String openId); }
package com.yunche.novels.mapper; import com.yunche.novels.bean.User; import org.apache.ibatis.annotations.*; /** * @author yunche * @date 2019/04/05 */ @Mapper public interface UserMapper { @Insert("INSERT INTO user(user_name, password) VALUES(#{userName}, #{password}) ") @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") Integer storeUser(User user); @Select("SELECT COUNT(*) FROM user where user_name=#{name}") Integer checkUserNameIsExists(String name); }
Controller類。
package com.yunche.novels.controller; import com.yunche.novels.bean.User; import com.yunche.novels.service.UserService; import com.yunche.novels.service.impl.GitHubAuthServiceImpl; import com.yunche.novels.util.MD5Utils; import com.yunche.novels.util.StringHelper; import com.yunche.novels.vo.AuthUserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpSession; /** * @author yunche * @date 2019/04/04 */ @Controller public class AuthController { @Autowired private GitHubAuthServiceImpl authService; @Autowired private UserService userService; @GetMapping("/oauth/github/callback") public String authorizeForGitHub(@RequestParam("code") String code, @RequestParam("state") String state, HttpSession session) { MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("code", code); map.add("state", state); String token = authService.getToken(map); //獲取用戶在第三方的信息 AuthUserVO userVO = authService.getUserInfo(token); String openId = userVO.getId(); //註冊該openId if(!authService.checkIsExistsOpenId(openId)) { User u = new User(); String userName = userVO.getLogin(); //確保用戶的用戶名惟一 while (userService.IsExistsName(userName)) { userName += StringHelper.getRandomString(3); } u.setUserName(userName); //生成一個隨機的必定長度的字符串並使用MD5加密,因爲第三方的密碼不可用,故隨機。 u.setPassword(MD5Utils.getMD5(StringHelper.getRandomString(16))); //註冊用戶 if(userService.insertUser(u)) { //將本地用戶與OpenId相關聯 if(authService.storeOpenIdByUser(openId, u.getId())) { //存儲用戶session session.setAttribute("user", u.getUserName()); } } } else { session.setAttribute("user", authService.getUserNameByOpenId(openId)); } // 重定向到以前須要受權的頁面 return "redirect:" + state; } }