SpringBoot+Vue實現第三方QQ登錄(二)


1 准備工作_OAuth2.0

本步驟的作用

  接入QQ登錄前,網站需首先進行申請,獲得對應的appid與appkey,以保證后續流程中可正確對網站與用戶進行驗證與授權。

本步驟在整個流程中的位置
oauth2.0_guid_1.png

1.1 申請appid和appkey

  appid:應用的唯一標識。在OAuth2.0認證過程中,appid的值即為oauth_consumer_key的值。

  appkey:appid對應的密鑰,訪問用戶資源時用來驗證應用的合法性。在OAuth2.0認證過程中,appkey的值即為oauth_consumer_secret的值。

1.2 申請地址

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

1.3 申請流程

1. 開發者資質審核

  參考文章:開發者注冊流程

2. 申請appid(oauth_consumer_key/client_id)和appkey(auth_consumer_secret/client_secret)。

  (1)進入https://connect.qq.com/manage.html#/頁面,點擊“創建應用”,在彈出的對話框中填寫網站或應用的詳細資料(名稱,域名,回調地址)。

  (2)點擊“確定”按鈕,提交資料后,獲取appid和appkey。

    注意:申請appid時,登錄的QQ號碼將與申請到的appid綁定,后續維護均需要使用該號碼。

    注意:對appid和appkey信息進行保密,不要隨意泄漏。

1.4 保證連接暢通

  接入QQ登錄時,網站需要不停的和Qzone進行交互,發送請求和接受響應。

  1. 對於PC網站:

    請在你的服務器上ping graph.qq.com ,保證連接暢通。

  2. 移動應用無需此步驟

2 放置“QQ登錄”按鈕_OAuth2.0

本步驟的作用

  在網站頁面上放置“QQ登錄”按鈕,並為按鈕添加前台代碼,實現點擊按鈕即彈出QQ登錄對話框 。

本步驟在整個流程中的位置
oauth2.0_guid_2.png

2.1 下載“QQ登錄”按鈕圖片,並將按鈕放置在頁面合適的位置

  按鈕圖片下載: 點擊這里下載 。

  可以到阿里矢量圖庫下載更多圖標:阿里巴巴矢量圖標庫 。

  按照UI規范,將按鈕放置在頁面合適的位置:點擊這里查看 。

2.2 為“QQ登錄”按鈕添加前台代碼

2.2.1 效果演示

2.2.2 前端代碼(Vue)

  為了實現上述效果,應該為“QQ登錄”按鈕圖片添加如下前台代碼:

<div style="line-height: 22px;margin:0 0 8px 0;color: #9b9b9b;">
        <span style="vertical-align:middle">第三方登錄:</span>
        <img :src="qqIcon" width="22" height="22" style="vertical-align:middle;margin-left: 2px" title="QQ" @click="handleQqLogin">
        <a href=""><img :src="weixinIcon" width="22" height="22" style="vertical-align:middle;margin-left: 2px" title="微信"></a>
        <img :src="weiboIcon" width="22" height="22" style="vertical-align:middle;margin-left: 2px" title="微博" @click="handleWeiBoLogin">
        <a href=""><img :src="qyweixinIcon" width="22" height="22" style="vertical-align:middle;margin-left: 2px" title="企業微信"></a>
</div>

 

 // qq登錄
// https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=1042&redirect_uri=https%3A%2F%2Fwww.lovezmy.link%2FqqCallback&state=3c6bc7df93a3c handleQqLogin() { getQQCode().then(res => { window.location.href = res; }) },

2.2.2 后端代碼(Java)

  qq登錄配置文件信息:

# QQ登錄配置
qq.appID = 116666602(替換成你的)
qq.appKEY = c04360a666666666666666c06(替換成你的)
qq.redirectURI = https://www.lovezmy.link/qqCallback(替換成你的)
qq.scope = get_user_info,add_topic,add_one_blog,add_album,upload_pic,list_album,add_share,check_page_fans,add_t,add_pic_t,del_t,get_repost_list,get_info,get_other_info,get_fanslist,get_idollist,add_idol,del_ido,get_tenpay_addr(\u8BF7\u4FEE\u6539\u6B64\u5904)
qq.baseURL = https://graph.qq.com/
qq.userInfoURL = https://graph.qq.com/user/get_user_info
qq.accessTokenURL = https://graph.qq.com/oauth2.0/token
qq.authorizeURL = https://graph.qq.com/oauth2.0/authorize
qq.openIDURL = https://graph.qq.com/oauth2.0/me
qq.addTopicURL = https://graph.qq.com/shuoshuo/add_topic
qq.addBlogURL = https://graph.qq.com/blog/add_one_blog
qq.addAlbumURL = https://graph.qq.com/photo/add_album
qq.uploadPicURL = https://graph.qq.com/photo/upload_pic
qq.listAlbumURL = https://graph.qq.com/photo/list_album
qq.addShareURL = https://graph.qq.com/share/add_share
qq.checkPageFansURL = https://graph.qq.com/user/check_page_fans
qq.addTURL = https://graph.qq.com/t/add_t
qq.addPicTURL = https://graph.qq.com/t/add_pic_t
qq.delTURL = https://graph.qq.com/t/del_t
qq.weiboUserInfoURL = https://graph.qq.com/user/get_info
qq.otherUserInfoURL = https://graph.qq.com/user/get_other_info
qq.fansListURL = https://graph.qq.com/relation/get_fanslist
qq.idolsListURL = https://graph.qq.com/relation/get_idollist
qq.addIdolURL = https://graph.qq.com/relation/add_idol
qq.delIdolURL = https://graph.qq.com/relation/del_idol
qq.tenpayAddrURL = https://graph.qq.com/cft_info/get_tenpay_addr
qq.repostListURL = https://graph.qq.com/t/get_repost_list
qq.version = 2.0.0.0

  讀取配置文件信息常量類:

package com.modules.security.constants;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * QQ登陸常量配置類
 */

@Data
@Configuration
@PropertySource("classpath:thirdparty/config.properties")  // 指定配置文件的路徑,屬性文件需放在根目錄的resources文件夾下面,才能被讀取出來
public class QQConstants {

    @Value("${qq.appID}")
    private String appID;

    @Value("${qq.appKEY}")
    private String appKEY;

    @Value("${qq.redirectURI}")
    private String redirectURI;

    @Value("${qq.authorizeURL}")
    private String authorizeURL;

    @Value("${qq.accessTokenURL}")
    private String accessTokenURL;

    @Value("${qq.openIDURL}")
    private String openIDURL;

    @Value("${qq.userInfoURL}")
    private String userInfoURL;

}

  Conteoller(獲取QQ登錄的url)

   /**
     * 獲得跳轉到qq登錄頁的url,前台直接a連接訪問
     *
     * @return
     * @throws Exception
     */
    @LogAnnotation("獲得跳轉到qq登錄頁的url")
    @ApiOperation("獲得跳轉到qq登錄頁的url")
    @AnonymousAccess
    @GetMapping("/getQQCode")
    public ResponseEntity<Object> getCode() throws Exception {
        // 授權地址 ,進行Encode轉碼
        String authorizeURL = qqConstants.getAuthorizeURL();

        // 回調地址 ,進行Encode轉碼
        String redirectUri = qqConstants.getRedirectURI();

        //用於第三方應用防止CSRF攻擊
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        // 保存到Redis
        redisUtils.set(QQSTATE + "-" + uuid, uuid, expiration, TimeUnit.MINUTES);

        // 拼接url
        StringBuilder url = new StringBuilder();
        url.append(authorizeURL);
        url.append("?response_type=code");
        url.append("&client_id=" + qqConstants.getAppID());
        // 轉碼
        url.append("&redirect_uri=" + URLEncodeUtil.getURLEncoderString(redirectUri));
        url.append("&state=" + uuid);

        return ResponseEntity.ok(url);
    }

3 使用Authorization_Code獲取Access_Token

本步驟的作用

  通過用戶驗證登錄和授權,獲取Access Token,為下一步獲取用戶的OpenID做准備。

  同時,Access Token是應用在調用OpenAPI訪問和修改用戶數據時必須傳入的參數。

移動端應用可以直接獲得AccessToken,請參考使用Implicit_Grant方式獲取Access_Token

本步驟在整個流程中的位置

oauth2.0_guid_3.png

3.1 簡介

  即server-side模式,是OAuth2.0認證的一種模式,又稱Web Server Flow。

  適用於需要從web server訪問的應用,例如Web網站。

對於應用而言,需要進行兩步:

  1. 獲取Authorization Code。

  2. 通過Authorization Code獲取Access Token。

3.2 獲取Authorization Code

請求地址

  PC網站:https://graph.qq.com/oauth2.0/authorize

請求方法

  GET

請求參數

參數 是否必須 含義
response_type 必須 授權類型,此值固定為“code”。
client_id 必須 申請QQ登錄成功后,分配給應用的appid。
redirect_uri 必須 成功授權后的回調地址,必須是注冊appid時填寫的主域名下的地址,建議設置為網站首頁或網站的用戶中心。注意需要將url進行URLEncode。
state 必須 client端的狀態值。用於第三方應用防止CSRF攻擊,成功授權后回調時會原樣帶回。請務必嚴格按照流程檢查用戶與state參數狀態的綁定。
scope 可選 請求用戶授權時向用戶顯示的可進行授權的列表。
可填寫的值是API文檔中列出的接口,如果要填寫多個接口名稱,請用逗號隔開。
例如:scope=get_user_info,list_album,upload_pic
不傳則默認請求對接口get_user_info進行授權。
建議控制授權項的數量,只傳入必要的接口名稱,因為授權項越多,用戶越可能拒絕進行任何授權。
display 可選 PC網站接入時使用。用於展示的樣式。不傳則默認展示為PC下的樣式。如果傳入“mobile”,則展示為mobile端下的樣式。

返回說明

1. 如果用戶成功登錄並授權,則會跳轉到指定的回調地址,並在redirect_uri地址后帶上Authorization Code和原始的state值。如:  

  https://www.lovezmy.link/qqCallback?code=1E83738E79B0CEBF13FC7C3B1224D9B3C&state=cca3c152c527229409cccd889ad2963fe

  注意:此code會在10分鍾內過期。

2. 如果用戶在登錄授權過程中取消登錄流程,對於PC網站,登錄頁面直接關閉。

錯誤碼說明

  接口調用有錯誤時,會返回code和msg字段,以url參數對的形式返回,value部分會進行url編碼(UTF-8)。

  PC網站接入時,錯誤碼詳細信息請參見:100000-100031:PC網站接入時的公共返回碼

3.3 通過Authorization Code獲取Access Token

請求地址

  PC網站:https://graph.qq.com/oauth2.0/token

請求方法

  GET

請求參數

參數 是否必須 含義
grant_type 必須 授權類型,在本步驟中,此值為“authorization_code”。
client_id 必須 申請QQ登錄成功后,分配給網站的appid。
client_secret 必須 申請QQ登錄成功后,分配給網站的appkey。
code 必須 上一步返回的authorization code。
如果用戶成功登錄並授權,則會跳轉到指定的回調地址,並在URL中帶上Authorization Code。
例如,回調地址為www.qq.com/my.php,則跳轉到:
http://www.qq.com/my.php?code=520DD95263C1CFEA087******
注意此code會在10分鍾內過期。
redirect_uri 必須 與上面一步中傳入的redirect_uri保持一致。
fmt 可選 因歷史原因,默認是x-www-form-urlencoded格式,如果填寫json,則返回json格式

返回說明

  如果成功返回,即可在返回包中獲取到Access Token。 如(不指定fmt時):

  access_token=FE04************CCE2&expires_in=7776000&refresh_token=88E4****************BE14

參數說明 描述
access_token 授權令牌,Access_Token。
expires_in 該access token的有效期,單位為秒。
refresh_token 在授權自動續期步驟中,獲取新的Access_Token時需要提供的參數。

注:refresh_token僅一次有效

錯誤碼說明

  接口調用有錯誤時,會返回code和msg字段,以url參數對的形式返回,value部分會進行url編碼(UTF-8)。

  PC網站接入時,錯誤碼詳細信息請參見:100000-100031:PC網站接入時的公共返回碼

接口代碼:

   /**
     * 獲得token信息(授權,每個用戶的都不一致) --> 獲得token信息該步驟返回的token期限為一個月
     *
     * @return
     * @throws Exception
     */
    public Map<String, Object> getToken(String code) throws Exception {
        StringBuilder url = new StringBuilder();
        url.append(qqConstants.getAccessTokenURL());
        url.append("?grant_type=authorization_code");
        url.append("&client_id=" + qqConstants.getAppID());
        url.append("&client_secret=" + qqConstants.getAppKEY());
        url.append("&code=" + code);

        // 回調地址
        String redirectUri = qqConstants.getRedirectURI();
        // 轉碼
        url.append("&redirect_uri=" + URLEncodeUtil.getURLEncoderString(redirectUri));
        // 獲得token
        String result = HttpClientUtils.get(url.toString(), "UTF-8");
        // 把token保存
        String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(result, "&");
        String accessToken = StringUtils.substringAfterLast(items[0], "=");
        Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
        String refreshToken = StringUtils.substringAfterLast(items[2], "=");
        //token信息
        Map<String, Object> qqProperties = new HashMap<String, Object>();
        qqProperties.put("accessToken", accessToken);
        qqProperties.put("expiresIn", String.valueOf(expiresIn));
        qqProperties.put("refreshToken", refreshToken);
        return qqProperties;
    }

3.4(可選)權限自動續期,獲取Access Token

  Access_Token的有效期默認是3個月,過期后需要用戶重新授權才能獲得新的Access_Token。本步驟可以實現授權自動續期,避免要求用戶再次授權的操作,提升用戶體驗。

請求地址

  PC網站:https://graph.qq.com/oauth2.0/token

請求方法

  GET

請求參數

參數 是否必須 含義
grant_type 必須 授權類型,在本步驟中,此值為“refresh_token”。
client_id 必須 申請QQ登錄成功后,分配給網站的appid。
client_secret 必須 申請QQ登錄成功后,分配給網站的appkey。
refresh_token 必須 首次:使用在Step2中獲取到的最新的refresh_token。

后續:使用刷新后返回的最新refresh_token

fmt 可選 因歷史原因,默認是x-www-form-urlencoded格式,如果填寫json,則返回json格式

返回說明

  如果成功返回,即可在返回包中獲取到Access Token。 如(不指定fmt時):

  access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14。

參數說明 描述
access_token 授權令牌,Access_Token。
expires_in 該access token的有效期,單位為秒。
refresh_token 在授權自動續期步驟中,獲取新的Access_Token時需要提供的參數。每次生成最新的refresh_token,且僅一次有效

錯誤碼說明

  接口調用有錯誤時,會返回code和msg字段,以url參數對的形式返回,value部分會進行url編碼(UTF-8)。

  PC網站接入時,錯誤碼詳細信息請參見:100000-100031:PC網站接入時的公共返回碼

接口代碼:

   /**
     * 刷新token 信息(token過期,重新授權)
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/refreshToken")
    public Map<String, Object> refreshToken(Map<String, Object> qqProperties) throws Exception {
        // 獲取refreshToken
        String refreshToken = (String) qqProperties.get("refreshToken");
        StringBuilder url = new StringBuilder(qqConstants.getAccessTokenURL());
        url.append("?grant_type=refresh_token");
        url.append("&client_id=" + qqConstants.getAppID());
        url.append("&client_secret=" + qqConstants.getAppKEY());
        url.append("&refresh_token=" + refreshToken);
        String result = HttpClientUtils.get(url.toString(), "UTF-8");
        // 把新獲取的token存到map中
        String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(result, "&");
        String accessToken = StringUtils.substringAfterLast(items[0], "=");
        Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
        String newRefreshToken = StringUtils.substringAfterLast(items[2], "=");
        //重置信息
        qqProperties.put("accessToken", accessToken);
        qqProperties.put("expiresIn", String.valueOf(expiresIn));
        qqProperties.put("refreshToken", newRefreshToken);
        return qqProperties;
    }

4 獲取用戶OpenID_OAuth2.0

本步驟的作用

  通過輸入在上一步獲取的Access Token,得到對應用戶身份的OpenID。

  OpenID是此網站上或應用中唯一對應用戶身份的標識,網站或應用可將此ID進行存儲,便於用戶下次登錄時辨識其身份,或將其與用戶在網站上或應用中的原有賬號進行綁定。

本步驟在整個流程中的位置
oauth2.0_guid_4.png

請求地址:

  PC網站:https://graph.qq.com/oauth2.0/me

請求方法:

  GET

請求參數:

參數 是否必須 含義
access_token 必須 在Step1中獲取到的access token。
fmt 可選 因歷史原因,默認是jsonpb格式,如果填寫json,則返回json格式

返回說明:

  PC網站接入時,獲取到用戶OpenID,返回包如下(如果fmt參數未指定):

  callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );

  openid是此網站上唯一對應用戶身份的標識,網站可將此ID進行存儲便於用戶下次登錄時辨識其身份,或將其與用戶在網站上的原有賬號進行綁定。

錯誤碼說明:

  接口調用有錯誤時,會返回code和msg字段,以url參數對的形式返回,value部分會進行url編碼(UTF-8)。

  PC網站接入時,錯誤碼詳細信息請參見:100000-100031:PC網站接入時的公共返回碼

接口代碼:

   /**
     * 獲取用戶openId(根據token)
     *
     * @return
     * @throws Exception
     */
    public String getOpenId(Map<String, Object> qqProperties) throws Exception {
        // 獲取保存的用戶的token
        String accessToken = (String) qqProperties.get("accessToken");
        if (StringUtils.isEmpty(accessToken)) {
            throw new BadRequestException("QQ登錄信息異常,請重試!!!");
        }
        StringBuilder url = new StringBuilder(qqConstants.getOpenIDURL());
        url.append("?access_token=" + accessToken);
        String result = HttpClientUtils.get(url.toString(), "UTF-8");
        String openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
        return openId;
    }

5 OpenAPI調用說明_OAuth2.0

本步驟的作用

  獲取到Access Token和OpenID后,可通過調用OpenAPI來獲取或修改用戶個人信息。

本步驟在整個流程中的位置
oauth2.0_guid_5.png

5.1 前提說明

1. 該appid已經開通了該OpenAPI的使用權限。

  從API列表的接口列表中可以看到,有的接口是完全開放的,有的接口則需要提前提交申請,以獲取訪問權限。

2. 准備訪問的資源是用戶授權可訪問的。

  網站調用該OpenAPI讀寫某個openid(用戶)的信息時,必須是該用戶已經對你的appid進行了該OpenAPI的授權(例如用戶已經設置了相冊不對外公開,則網站是無法讀取照片信息的)。

  用戶可以進入手機QQ->設置->隱私->授權管理,進行訪問權限管理。

3. 已經成功獲取到Access Token,並且Access Token在有效期內。

5.2 調用OpenAPI接口

  QQ登錄提供了用戶信息等OpenAPI(詳見API列表),網站需要將請求發送到某個具體的OpenAPI接口,以訪問或修改用戶數據。

  調用所有OpenAPI時,除了各接口私有的參數外,所有OpenAPI都需要傳入基於OAuth2.0協議的通用參數:

參數 含義
access_token 可通過使用Authorization_Code獲取Access_Token 或來獲取。access_token有3個月有效期。
oauth_consumer_key 申請QQ登錄成功后,分配給應用的appid
openid 用戶的ID,與QQ號碼一一對應。
可通過調用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 來獲取。

5.3 示例

1. 以get_user_info接口為例:(請將access_token,appid等參數值替換為你自己的)

  https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID

2. 成功返回后,即可獲取到用戶數據:

package com.modules.security.entity;

import lombok.Data;

import java.io.Serializable;

/**
 * QQ登錄用戶信息
 * @author Liyh
 * @date 2020/12/22
 */

@Data
public class QQUserInfo implements Serializable {

    private String ret;                  // 返回碼
    private String msg;                  // 如果ret<0,會有相應的錯誤信息提示,返回數據全部用UTF-8編碼。
    private String nickname;             // 用戶在QQ空間的昵稱。
    private String figureurl;            // 大小為30×30像素的QQ空間頭像URL。
    private String figureurl_1;          // 大小為50×50像素的QQ空間頭像URL。
    private String figureurl_2;          // 大小為100×100像素的QQ空間頭像URL。
    private String figureurl_qq_1;       // 大小為40×40像素的QQ頭像URL。
    private String figureurl_qq_2;       // 大小為100×100像素的QQ頭像URL。需要注意,不是所有的用戶都擁有QQ的100x100的頭像,但40x40像素則是一定會有。
    private String gender;               // 性別。 如果獲取不到則默認返回"男"
    private Integer gendertype;          // 性別 數字
    private String is_yellow_vip;        // 標識用戶是否為黃鑽用戶(0:不是;1:是)。
    private String vip;                  // 標識用戶是否為黃鑽用戶(0:不是;1:是)
    private String yellow_vip_level;     // 黃鑽等級
    private String level;                // QQ等級
    private String is_yellow_year_vip;   // 標識是否為年費黃鑽用戶(0:不是; 1:是)
    private String province;             // 省
    private String city;                 // 市

}

3. 接口代碼:

   /**
     * 根據token,openId獲取用戶信息
     */
    public QQUserInfo getUserInfo(Map<String, Object> qqProperties) throws Exception {
        // 取出token
        String accessToken = (String) qqProperties.get("accessToken");
        String openId = (String) qqProperties.get("openId");
        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openId)) {
            throw new BadRequestException("QQ登錄信息異常,請重試!!!");
        }
        // 拼接url
        StringBuilder url = new StringBuilder(qqConstants.getUserInfoURL());
        url.append("?access_token=" + accessToken);
        url.append("&oauth_consumer_key=" + qqConstants.getAppID());
        url.append("&openid=" + openId);
        // 獲取qq相關數據
        String result = HttpClientUtils.get(url.toString(), "UTF-8");
        Object json = JSON.parseObject(result, QQUserInfo.class);
        QQUserInfo userInfo = (QQUserInfo) json;
        return userInfo;
    }

6 個人網站(YOUYOUSHOP)(用戶名:admin,密碼:adminliyh),QQ,微博等已經實現,需要的小伙伴可以測試 

6.1 每個人做的項目需求不同,可能會出現不同的問題,文章可以參考,也可以留言你的問題,我會幫你解決,大家一起加油

 


免責聲明!

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



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