第三方登錄:QQ登錄實現(OAuth2.0)


一、創建應用

1、在 QQ互聯 創建應用

地址:https://connect.qq.com/manage.html#/

然后進行實名認證,創建應用,審核通過

 

SpringBoot網站添加第三方登錄之QQ登錄

 

然后點擊查看,可以獲得 APP ID 和 APP Key

SpringBoot網站添加第三方登錄之QQ登錄

 

回調地址如下

SpringBoot網站添加第三方登錄之QQ登錄

 

2、授權的基本原理

可以參考官方文檔

1)根據QQ登錄鏈接可以回調獲得 code

2)根據APP ID 、APP Key 和 code 可獲得 token

3)根據 token 獲得 OpenId

4)  根據 OpenId 可以獲得用戶的基本信息

SpringBoot網站添加第三方登錄之QQ登錄

其中 OpenId 是一個唯一的值,比如 8A674574E1B12345D790A1B3EFE81234

 

3、幾個必要的URL

1)登錄頁面授權 URL:

https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s

2)獲得 Token 的 URL:

https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s

3)獲得用戶OpenId 的 URL:

https://graph.qq.com/oauth2.0/me?access_token=%s

4)獲得用戶信息的 URL:

https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s

 

 

二、基本准備

1、數據庫設計

主要看 bind 和 bind_type 表的數據

SpringBoot網站添加第三方登錄之QQ登錄

identifier 表示識別碼,保證唯一性

credential 表示憑證,通常用不上

bind_type_id 和 user_id 是外鍵,很好理解

 

SpringBoot網站添加第三方登錄之QQ登錄

name 唯一性

style 樣式

 

2、放置 QQ 登錄按鈕

這里有兩種常用的方式

① 彈小窗

 
  1. <script>
  2.     function openWin(url,name,iWidth,iHeight) {
  3.         //獲得窗口的垂直位置
  4.         var iTop = (window.screen.availHeight - 30 - iHeight) / 2;
  5.         //獲得窗口的水平位置
  6.         var iLeft = (window.screen.availWidth - 10 - iWidth) / 2;
  7.         window.open(url, name, 'height=' + iHeight + ',innerHeight=' + iHeight + ',width=' + iWidth + ',innerWidth=' + iWidth + ',top=' + iTop + ',left=' + iLeft + ',status=no,toolbar=no,menubar=no,location=no,resizable=no,scrollbars=0,titlebar=no');
  8.     }
  9.     function qqLogin() {
  10.         var url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101472393&redirect_uri=http://codergroup.cn/oauth/qq/callback&scope=get_user_info";
  11.         openWin(url,"qqLogin",650,500);
  12.     }
  13. </script>
  14. <a  href="javascript:void(0);" onclick="qqLogin()"></a>

 

SpringBoot網站添加第三方登錄之QQ登錄

② 在新窗口打開

 
  1. <href="https://graph.qq.com/oauth2.0/authorizeresponse_type=code&client_id=101472393&redirect_uri=http://codergroup.cn/oauth/qq/callback&scope=get_user_info" target="_blank"></a>

 

三、具體代碼

參考這里

1、由於做了多個登錄,所以代碼做了一定程度的封裝,大致如下:

 
  1. //多個登錄差不多都要共用這些方法,所以統一放到這個接口中
  2. public interface AuthService {
  3.     public abstract String getAccessToken(String code);
  4.     public abstract String getOpenId(String accessToken);
  5.     public abstract String refreshToken(String code);
  6.     public abstract String getAuthorizationUrl() throws UnsupportedEncodingException;
  7.     public abstract JSONObject getUserInfo(String accessToken,String openId);
  8. }

 

2、由於全部是自己封裝的,所以http請求的代碼也是所有的登錄共用的,這里統一放放到了類DefaultAuthServiceImpl中,代碼如下:

 
  1. public abstract class DefaultAuthServiceImpl implements AuthService{
  2.     public static RestTemplate getRestTemplate() {// 手動添加
  3.         SimpleClientHttpRequestFactory requestFactory=new SimpleClientHttpRequestFactory();
  4.         requestFactory.setReadTimeout(120000);
  5.         List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
  6.         messageConverters.add(new ByteArrayHttpMessageConverter());
  7.         messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
  8.         messageConverters.add(new ResourceHttpMessageConverter());
  9.         messageConverters.add(new SourceHttpMessageConverter<Source>());
  10.         messageConverters.add(new AllEncompassingFormHttpMessageConverter());
  11.         messageConverters.add(new MappingJackson2HttpMessageConverter());
  12.         RestTemplate restTemplate=new RestTemplate(messageConverters);
  13.         restTemplate.setRequestFactory(requestFactory);
  14.         return restTemplate;
  15.     }
  16. }

由此,所有的登錄Service只需要繼承AuthService即可。

 

3、QQ登錄 Service 接口

QQAuthService.java

 
  1. public interface QQAuthService extends AuthService {
  2. }

 

4、QQ登錄 Service 實現

QQAuthServiceImpl.java

 
  1. @Service
  2. public class QQAuthServiceImpl extends DefaultAuthServiceImpl implements QQAuthService {
  3.     private Logger logger = LoggerFactory.getLogger(QQAuthServiceImpl.class);
  4.     //QQ 登陸頁面的URL
  5.     private final static String AUTHORIZATION_URL =
  6.             "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s";
  7.     //獲取token的URL
  8.     private final static String ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";
  9.     // 獲取用戶 openid 的 URL
  10.     private static final String OPEN_ID_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
  11.     // 獲取用戶信息的 URL,oauth_consumer_key 為 apiKey
  12.     private static final String USER_INFO_URL = "https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s";
  13.     // 下面的屬性可以通過配置讀取
  14.     private  static final String CALLBACK_URL = "http://XXX/XX/XX"; // QQ 在登陸成功后回調的 URL,這個 URL 必須在 QQ 互聯里填寫過
  15.     private  static final String API_KEY  = "xxxxxx";                                      // QQ 互聯應用管理中心的 APP ID
  16.     private  static final String API_SECRET = "xxxxxx";               // QQ 互聯應用管理中心的 APP Key
  17.     private  static final String SCOPE       = "get_user_info";                                  // QQ 互聯的 API 接口,訪問用戶資料
  18.     /**
  19.      * @return QQ 登陸頁面的 URL
  20.      */
  21.     @Override
  22.     public String getAuthorizationUrl() {
  23.         String url = String.format(AUTHORIZATION_URL,API_KEY,CALLBACK_URL,SCOPE);
  24.         return url;
  25.     }
  26.     @Override
  27.     public String getAccessToken(String code) {
  28.         String url = String.format(ACCESS_TOKEN_URL,API_KEY,API_SECRET,code, CALLBACK_URL);
  29.         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
  30.         URI uri = builder.build().encode().toUri();
  31.         String resp = getRestTemplate().getForObject(uri, String.class);
  32.         logger.error("getAccessToken resp = "+resp);
  33.         if(resp.contains("access_token")){
  34.             Map<String,String> map = getParam(resp);
  35.             String access_token = map.get("access_token");
  36.             return access_token;
  37.         }else{
  38.             throw new ServiceException(resp);
  39.         }
  40.     }
  41.     //由於QQ的幾個接口返回類型不一樣,此處是獲取key-value類型的參數
  42.     private Map<String,String> getParam(String string){
  43.         Map<String,String> map = new HashMap();
  44.         String[] kvArray = string.split("&");
  45.         for(int i = 0;i<kvArray.length;i++){
  46.             String[] kv = kvArray[i].split("=");
  47.             map.put(kv[0],kv[1]);
  48.         }
  49.         return map;
  50.     }
  51.     //QQ接口返回類型是text/plain,此處將其轉為json
  52.     public JSONObject ConvertToJson(String string){
  53.         string = string.substring(string.indexOf("(")+1,string.length());
  54.         string = string.substring(0,string.indexOf(")"));
  55.         logger.error("ConvertToJson s = "+string);
  56.         JSONObject jsonObject = JSONObject.parseObject(string);
  57.         return jsonObject;
  58.     }
  59.     @Override
  60.     public String getOpenId(String accessToken) {
  61.         String url = String.format(OPEN_ID_URL,accessToken);
  62.         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
  63.         URI uri = builder.build().encode().toUri();
  64.         String resp = getRestTemplate().getForObject(uri, String.class);
  65.         logger.error("getAccessToken resp = "+resp);
  66.         if(resp.contains("openid")){
  67.             JSONObject jsonObject = ConvertToJson(resp);
  68.             String openid = jsonObject.getString("openid");
  69.             return openid;
  70.         }else{
  71.             throw new ServiceException(resp);
  72.         }
  73.     }
  74.     /**
  75.      * https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID
  76.      * @param accessToken
  77.      * @param openId
  78.      * @return
  79.      */
  80.     @Override
  81.     public JSONObject getUserInfo(String accessToken, String openId){
  82.         openId = getOpenId(accessToken);
  83.         String url = String.format(USER_INFO_URL,accessToken, API_KEY, openId);
  84.         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
  85.         URI uri = builder.build().encode().toUri();
  86.         String resp = getRestTemplate().getForObject(uri,String.class);
  87.         JSONObject data = JSONObject.parseObject(resp);
  88.         logger.error("resp = "+data);
  89.         JSONObject result = new JSONObject();
  90.         result.put("id",openId);
  91.         result.put("nickName",data.getString("nickname"));
  92.         result.put("avatar",data.getString("figureurl_qq_1"));
  93.         return result;
  94.     }
  95.     @Override
  96.     public String refreshToken(String code) {
  97.         return null;
  98.     }
  99. }

 

5、在Controller中調用,代碼如下:

 
  1. @RestController
  2. @RequestMapping(value = "/oauth")
  3. public class AuthController {
  4.      private Logger logger = LoggerFactory.getLogger(AuthController.class);
  5.       @Autowired
  6.     private QQAuthService qqAuthService;
  7.     @Autowired
  8.     private UserService userService;
  9.  //訪問登陸頁面,然后會跳轉到 QQ 的登陸頁面
  10.     @RequestMapping(value = "/qqLoginPage",method = RequestMethod.GET)
  11.     public JSONObject qqLogin() throws Exception {
  12.         String uri = qqAuthService.getAuthorizationUrl();
  13.         return loginPage(uri);
  14.     }
  15. }
  16. //qq授權后會回調此方法,並將code傳過來
  17. @RequestMapping("/qq/callback")
  18.     public void getQQCode(String code, HttpServletRequest request,HttpServletResponse response) throws Exception {
  19.         //根據code獲取token
  20.         String accessToken = qqAuthService.getAccessToken(code);
  21.         // 保存 accessToken 到 cookie,過期時間為 30 天,便於以后使用
  22.         Cookie cookie = new Cookie("accessToken", accessToken);
  23.         cookie.setMaxAge(60 * 24 * 30);
  24.         response.addCookie(cookie);
  25.         //本網站是將用戶的唯一標識存在用戶表中,大家也可以加一張表,存儲用戶和QQ的對應信息。
  26.         //根據openId判斷用戶是否已經綁定過
  27.         String openId = qqAuthService.getOpenId(accessToken);
  28.         KmsUser user = userService.getUserByCondition(openId);
  29.         if (user == null) {
  30.         //如果用戶不存在,則跳轉到綁定頁面
  31.             response.sendRedirect(request.getContextPath() + "/bind?type="+Constants.LOGIN_TYPE_QQ);
  32.         } else {
  33.             //如果用戶已存在,則直接登錄
  34.             response.sendRedirect(request.getContextPath());
  35.         }
  36.     }

 

查看更多

SpringBoot網站添加第三方登錄之GitHub登錄

 

本文轉載自

原文作者:言曌博客

原文鏈接:https://liuyanzhao.com/8210.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM