基於 Spring Boot + Vue.js + MySQL 的 QQ 登陸實戰

2021-03-06 Java後端

如何能在自己的網站上接入 QQ 登錄功能?這篇文章就是解決這個事情。此文基於OAuth2 協議開發 QQ 聯合登錄實戰過程,在學習本篇內容前您需要提前了解:

前後端分離開發模式

vue.js 基礎語法 比如 axios、事件綁定等相關知識

後端開發、資料庫等相關基礎知識

如果您已經具備了以上所述那我們就開搞吧!

OAuth2.0 是一個開放協議的標準,該標準允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密資源(如頭像、照片、視頻等),而在這個過程中無需將用戶名和密碼提供給第三方應用。實現這一功能是通過提供一個令牌(token),而不是使用用戶名和密碼來訪問他們存放在特定服務提供者的數據。採用令牌(token)的方式可以讓用戶靈活的對第三方應用授權或者收回權限。

對於大家而言,我們在網際網路應用中最常見的 OAuth2 應該就是各種第三方登錄了,例如 QQ 授權登錄、微信授權登錄、微博授權登錄、GitHub 授權登錄等等
oauth2 的認證流程如圖。

OAuth2中有4種授權模式

1. 簡化模式:簡化模式適用於純靜態頁面應用。所謂純靜態頁面應用,也就是應用沒有在伺服器上執行代碼的權限(通常是把代碼託管在別人的伺服器上),只有前端 JS 代碼的控制權。這種場景下,應用是沒有持久化存儲的能力的。因此,按照 oAuth2.0 的規定,這種應用是拿不到 Refresh Token 的。其整個授權流程如下:
2. 授權碼模式:授權碼模式適用於有自己的伺服器的應用,它是一個一次性的臨時憑證,用來換取 access_token 和 refresh_token,認證伺服器提供了一個類似這樣的接口:https://www.funtl.com/exchange?code=&client_id=&client_secret=需要傳入 code、client_id 以及 client_secret。驗證通過後,返回 access_token 和 refresh_token。一旦換取成功,code 立即作廢,不能再使用第二次。流程圖如下:
3. 密碼模式: 密碼模式中用戶向客戶端提供自己的用戶名和密碼。客戶端使用這些信息,向 "服務商提供商" 索要授權。在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在用戶對客戶端高度信任的情況下,比如客戶端是作業系統的一部分認證流程如下:

4. 客戶端模式: 如果信任關係再進一步,或者調用者是一個後端的模塊,沒有用戶界面的時候,可以使用客戶端模式。鑑權伺服器直接對客戶端進行身份驗證,驗證通過後,返回 token。認證流程如下:

以上做出了筆者對於 OAuth2.0 協議的理解,關於 OAuth2.0 協議的詳細內容在此不擴展了 可訪問 騰訊開放平臺 https://wiki.connect.qq.com/ 查閱更詳細的介紹,本文在此選擇使用4中認證模式中的授權碼模式進行案例開發,這個模式也是最安全最常用的模式.

2. 開發前閱讀文檔


本篇內容主要完成網站接入以下4個步驟就是網站接入需要完成的4個步驟

1.生成授權連結,獲取 code 

GET https://graph.qq.com/oauth2.0/authorize?response_type=code& client_id=101420900&redirect_uri=http://127.0.0.1:10011/user/qqAuthenticationFallback& state=1234656

client_id 為用戶在騰訊開放平臺填寫的需要qq登陸的應用的 客戶端idredirect_uri 授權回調的地址,需要在騰訊開放平臺上填寫

2.根據 code 獲取 access_token 

GET https://graph.qq.com/oauth2.0/token?grant_type=authorization_code& client_id=101420900&client_secret=bd56a336f6ac49a65005595c2a41201a&code=E28F27AFC3D8A17B75F05E9661FB933E &redirect_uri=http://127.0.0.1:8764/mobile/qqLoginCallback

client_id 為用戶在騰訊開放平臺填寫的需要qq登陸的應用的 客戶端idredirect_uri 授權回調的地址,需要在騰訊開放平臺上填寫client_secret:為需要qq登陸的應用的客戶端密匙4. 根據 access_token 獲取 openId (qq登陸) 

GET https://graph.qq.com/oauth2.0/me?access_token=CF8775A510EA68ED8576C9F675B42862

5. 再由openId和access_token向騰訊伺服器換取用戶信息 

GET https://graph.qq.com/user/get_user_info? access_token=CF8775A510EA68ED8576C9F675B42862& oauth_consumer_key=12345& openid=537F314752DA3A491B4F66C04D6AD9FF

oauth_consumer_key:需要qq登陸的應用id3. 在騰訊開放平臺中創建應用了解看懂了網站接入開發的4個步驟之後那我們就進入開發準備吧!網站上的文檔說的很清晰了我們先要在開放平臺上創建應用後才能接入開發,以下是大致的應用創建過程

網站地址這裡要填寫一個標準的域名地址(可以填一個假的域名)回調地址 這裡是騰訊伺服器將參數回傳到你項目中的接口填寫好之後提交,提交後應用不會立即生效可用 應用是需要審核的審核時間一周內完成!應用創建好之後登錄騰訊開放平臺點擊應用管理查看應用審核狀態如圖:在騰訊開放平臺創建好應用之後就可以正式進入寫代碼階段4. 編碼與案例實現在閱讀官方文檔後我們知道認證和登錄的那4個步驟其實就是自己的網站和騰訊伺服器之間的http協議交互,所以我們需要在Java代碼中發送http請求,在此我們需要在騰訊平臺上下載 官方的 java 版的 sdk工具包,或者從筆者網盤中下載 網盤地址為:https://pan.baidu.com/s/1Cvg4tAiSPEzRAIbnwsPeEg  提取碼:zo0j在此聲明本篇文章實戰的案例的完整源碼是不能對外開放,所以需要讀者詳細閱讀完這篇案例文章了解本篇文章採用的方法和思路後自己動手寫出屬於自己的案例,本篇文章旨在介紹 QQ 聯合登錄方式中的一種思路和實現方式,重在思路,實現並不難。閒話不多說了,目前筆者有一個前後端分離的項目,前端項目使用 vue.js ,後端項目是一個 Spring Boot 項目,在這個項目中已經實現了用戶手機號登錄(新用戶自動註冊),登錄成功後,後端隨機生成的 token,並且 token 作為 key,用戶 id 作為 value 存入 redis 中,並把 token 返回給前端。現在我們需要再開發出 QQ 登錄功能,用戶登錄時可以選擇 QQ 登錄也可以使用手機號獲取驗證碼登錄。同一個用戶使用不同的帳號登錄同一個網站,那網站如何識別這兩種不同的帳號是同一個人的帳號呢?其實不難想到 ,使用一個中間值關聯兩種帳號。在 QQ 聯合登錄的4個步驟中我們知道 QQ 登錄成功後可以獲取到一個唯一的 openId,所以我們需要拿這個 openId 來綁定使用手機號登錄的用戶,那具體如何綁定呢?首先我們需要修改資料庫中現有的用戶表,添加一個欄位表示第三方登錄成功後獲取到的 openId,用戶表如下。

用戶點擊使用 QQ 登錄,登錄成功獲取到 openId 後,根據這個 openId 查詢用戶表是否存在相對應的用戶,如果有就返回該用戶即可,如果沒有那麼需要用戶綁定一個手機號,如果這個手機號是已經註冊的用戶,那把 openId 存入到已有帳戶數據中即可,如果用戶表中並不存在這個手機號創建的用戶則根據這個手機號和 openId 創建一個帳戶。思路明白之後我們就可以開始寫後端代碼了,此處只給出必要的代碼塊希望讀者能理解本文之後自己根據自己項目的業務設計出想要的效果,因為本項目是使用maven管理jar包依賴,所以需要把上文的sdk install 至本地maven倉庫使用命令:

mvn install:install-file -Dfile=F:/Sdk4J.jar -DgroupId=com.sdk4j -DartifactId=sdk4j -Dversion=1.0 -Dpackaging=jar

-Dfile= 指定需要install的jar包所在路徑執行成功後在SpringBoot項目的pom.xml文件添加如下依賴:

<dependency>
    <groupId>com.sdk4j</groupId>
    <artifactId>sdk4j</artifactId>
    <version>1.0</version>
</dependency>

在項目 resources 目錄下新建文件 qqconnectconfig.properties 文件內容如下:

app_ID = #客戶端appid
app_KEY = #appkey 密匙
redirect_URI = #回調地址
scope = get_user_info #授權範圍
baseURL = https://graph.qq.com/
getUserInfoURL = https://graph.qq.com/user/get_user_info
accessTokenURL = https://graph.qq.com/oauth2.0/token
authorizeURL = https://graph.qq.com/oauth2.0/authorize
getOpenIDURL = https://graph.qq.com/oauth2.0/me
addTopicURL = https://graph.qq.com/shuoshuo/add_topic
addBlogURL = https://graph.qq.com/blog/add_one_blog
addAlbumURL = https://graph.qq.com/photo/add_album
uploadPicURL = https://graph.qq.com/photo/upload_pic
listAlbumURL = https://graph.qq.com/photo/list_album
addShareURL = https://graph.qq.com/share/add_share
checkPageFansURL = https://graph.qq.com/user/check_page_fans
addTURL = https://graph.qq.com/t/add_t
addPicTURL = https://graph.qq.com/t/add_pic_t
delTURL = https://graph.qq.com/t/del_t
getWeiboUserInfoURL = https://graph.qq.com/user/get_info
getWeiboOtherUserInfoURL = https://graph.qq.com/user/get_other_info
getFansListURL = https://graph.qq.com/relation/get_fanslist
getIdolsListURL = https://graph.qq.com/relation/get_idollist
addIdolURL = https://graph.qq.com/relation/add_idol
delIdolURL = https://graph.qq.com/relation/del_idol
getTenpayAddrURL = https://graph.qq.com/cft_info/get_tenpay_addr
getRepostListURL = https://graph.qq.com/t/get_repost_list
version = 2.0.0.0

app_ID 就是在騰訊開放中心創建成功的應用idapp_KEY 就是在騰訊開放中心創建成功的應用密鑰redirect_URI 就是在騰訊開放中心創建應用的授權成功後調地址scope 參數可以查看官網文檔,這裡授權範圍 只需要獲取到用戶信息即可接下我們按照上文中認證和授權的4個步驟一步一步的寫代碼

1. 生成授權連結

//qq聯合登陸第一步
//controller 實現
@GetMapping("getAuthenticationUrl")
public Map<String,Object>getAuthenticationUrl(HttpServletRequest res) throws Exception {
    return userService.getAuthenticationUrl(type,res);
}
--
//service層實現 返回String授權地址給前端即可
@Override
public Map<String, Object> getAuthenticationUrl(HttpServletRequest res) {
    return BaseResponse.Result(EnumStatus.OK,result);
},

public String getAuthenticationUrl(HttpServletRequest request) throws QQConnectException {
    String authorizeURL = new Oauth().getAuthorizeURL(request);
    return authorizeURL;
}

前端vue.js實現,我們需要在登錄頁添加一個qq登錄的按鈕圖標,用戶點擊時調用下文ThirdLogin()函數發起請求,效果如圖:

ThirdLogin(){
  this.axios.get(getAuthenticationUrl).then(res=>{
    if(res.data.code===200){
    // 瀏覽器窗口打開後端返回的授權地址
    window.location.href= res.data.data
    }
    console.log(res)
  })
},

啟動前後端項目,點擊qq登錄按鈕 此時第一步成功完成的效果圖如下:

https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=101834111&redirect_uri=http://127.0.0.1:10011/user/qqAuthenticationFallback&response_type=code&state=69d3bfdc448eec21026cb500be570f93&scope=get_user_info

2. 編寫回調地址接口,根據code 獲取access_token 再根據access_token 獲取到用戶的信息。

@ResponseBody
@GetMapping("qqAuthenticationFallback")
public void qqAuthenticationFallback(HttpServletRequest request,HttpServletResponse res) throws QQConnectException, IOException {
    // 1.回調地址中會拼接著 code
    String code = request.getParameter("code");
    log.info("code="+code);
    // 2.通過 code 獲取授權碼,
    // AccessToken accessTokenObj = new Oauth().getAccessTokenByRequest(request);
   // 此方法不行啊 只能使用httpClient 發送請求獲取數據
    String url="https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id="+appId+"&client_secret="+appKey+"&code="+code+"&redirect_uri="+redirectURI;
    HttpGet httpGet = new HttpGet(url);
    CloseableHttpClient client= HttpClients.createDefault();
    CloseableHttpResponse response = client.execute(httpGet);
    HttpEntity entity = response.getEntity();
    byte[] bytes = EntityUtils.toByteArray(entity);
    String str = new String(bytes, "utf-8");
    String[] split = str.split("&");
    String[] split1 = split[0].split("=");
    // 3.再根據授權碼獲取到 access_token
    OpenID openID = new OpenID(split1[1]);
    String accessToken=split1[1];
    log.info("accessToken="+accessToken);
    String userOpenID = openID.getUserOpenID();
    // 4.通過令牌獲取用戶的 openId
    log.info("openId="+userOpenID);
    // 以上4步已經實現了qq聯合登陸
    //5.根據openId和accessToken從獲取用戶信息
    String token = userService.ThirdLogin(accessToken, userOpenID);
    res.sendRedirect("http://127.0.0.1:8080/#/login?token="+token);
}

這裡的 userService.ThirdLosing() 方法實現 可以發送 httpClient 請求獲取到用戶的信息(也就是你的 QQ 帳號信息),地址為上文4個步驟中的最後一步的請求地址,除了獲取到用戶信息之外,還要完成上文中我們提到的思路 即帳號之間的綁定和創建 這裡就不再贅述了,每個人項目都不同,要自己多思考哦。

res.sendRedirect("http://127.0.0.1:8080/#/login?token="+token)

這一步中也許很多朋友不理解,思路是這樣的,因為我們的 sdk 是服務端的 api 請求形式,在獲取到用戶信息之後 服務端 api 是無法通知前端登錄頁面做如何調轉,這裡的重定向就是通知前端界面調轉的 ,可以看到攜帶了 token 參數,當前端地址重定向後地址欄中會攜帶這個 token,前端需要獲取到這個 token,再發送請求根據這個 token 判斷對應的用戶是否綁定手機號,如果未綁定手機號則 前端需要調轉至用戶綁定手機號界面完成綁定。

至此本篇文章向大家展示了基於Oauth2.0協議完成 QQ 聯合登錄的案例,很多步驟需要朋友自己去思考和琢磨。

公眾號運營至今,離不開小夥伴們的支持。為了給小夥伴們提供一個互相交流的平臺,特地開通了官方交流群。掃描下方二維碼備註 進群 或者關注公眾號 Java後端 後獲取進群通道。

相關焦點

  • Spring boot 基於註解方式配置datasource
    Spring boot 基於註解方式配置datasourceXml配置我們先來回顧下,使用xml配置數據源。boot基於註解方式怎麼配置數據源。文件中查找前綴為mysql.core的後面為:jdbc-url這個可以。
  • Spring Boot 2.X 實戰--SQL 資料庫(MyBatis)
    Gradle 依賴 1dependencies { 2    implementation 'org.springframework.boot:spring-boot-starter-web' 3    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter
  • 2021 最新版 Spring Boot 速記教程
    <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId></dependency>
  • Spring Boot集成JDBCTemplate
    Spring Boot集成添加依賴首先添加依賴:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc
  • 【Vue.js入門到實戰教程】11-Vue Loader(下)| 編寫一個單文件 Vue 組件
    然後在 src/main.js 中引入 Bootstrap 的腳本和樣式文件:import Vue from 'vue'import App from '.編寫 ModalExample 組件我們將 vue_learning/component/slot.html 中的 modal-example 組件拆分出來,在 vue_learning/demo-project/src/components 目錄下新建一個單文件組件 ModalExample.vue,將 modal-example 組件代碼按照 Vue Loader 指定的格式填充到對應位置
  • Spring Boot2.2.2整合H2和MySQL自由切換數據源
    測試短動畫本文將介紹基於Spring Boot 2.2.2.RELEASE實現H2資料庫和MySQL資料庫兩個數據源的自由切換。在本文中,數據源實現使用阿里巴巴提供的Druid數據源。</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!
  • SpringBoot+Vue完整的外賣系統,手機端和後臺管理,可以玩一下!
    一個完整的外賣系統,包括手機端,後臺管理,api基於spring boot和vue的前後端分離的外賣系統包含完整的手機端,後臺管理功能本項目主要供交流學習,不建議商用。技術選型核心框架:Spring Boot資料庫層:Spring data jpa/Spring data mongodb資料庫連接池:Druid緩存:Ehcache前端:Vue.js資料庫:mysql5.5以上,Mongodb4.0(不要使用最新版4.2)模塊flash-waimai-mobile 手機端站點flash-waimai-manage後臺管理系統flash-waimai-api
  • Spring Boot 採用Sharding-JDBC 實現Mybaits的分庫分表功能
    業內其實也有很多比較成熟的解決方案,如:Cobar、Cobar-client、TDDL、Sharding-JDBC等,這次就拿當當網開源的Sharding-JDBC來實現基於Mybatis的分庫分表功能。
  • 新鮮出爐的一款SpringBoot +Vue的考試系統
    Vuex: 一個專為Vue.js 應用程式開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Element-UI: 一套為開發者、設計師和產品經理準備的基於Vue 2.0 的桌面端組件庫。vue-router: Vue.js 官方的路由管理器。axios: 一個基於Promise 的HTTP 庫,可以用在瀏覽器和node.js 中。
  • spring-boot-plus V1.2.3 發布,新增 CentOS 相關腳本
    Download the installation scriptInstall jdk, git, maven, redis, mysqlwget -O download-install-all.sh https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/download-install-all.sh
  • 【項目推薦】Vue.js
    作者是尤雨溪,寫下這篇文章時 vue.js版本為 1.0.7 。我推薦使用 sublime text 作為編輯器,關於這個編輯器可以看我這篇文章。 </div> </div> </div></body></html>當然你也可以在 github 上 clone 最新的版本並作為單文件引入,或者使用 CDN:http://cdn.jsdelivr.net/vue/1.0.7/vue.min.js
  • 史上最全spring boot實戰文檔,吃透這些,面試幹掉80%對手
    最大的重要性是:springcloud是一個基於springboot實現的一系 列框架的集合,用來提供全局的服務治理方案。springcloud要基於springboot來實現,離不開springboot。如果要學習源碼,當然還是SpringBoot最適合不過了。
  • 推薦一些 GitHub 上值得前端學習的開源實戰項目,進階必看!
    最近好多同學問我了解找一些學習的實戰項目;看一個別人寫的優秀的項目,從中可以學到很多;比如代碼的規範,項目的結構;從項目作者每次提交記錄,去學習一些別人的開發思維以及開發整個項目的流程;下面我主要找了一些比較火的一些框架以及 node 項目。
  • 【Vue.js 入門到實戰教程】01-Vue.js 數據綁定的基本實現和代碼分析
    來源 | https://xueyuanjun.com/post/21912本系列教程涵蓋 Vue 基礎語法、組件開發、代碼測試、以及如何基於
  • 基於Spring Boot和Spring Cloud實現微服務架構學習
    Spring Boot:旨在簡化創建產品級的 Spring 應用和服務,簡化了配置文件,使用嵌入式web伺服器,含有諸多開箱即用微服務功能,可以和spring cloud聯合部署。Spring Framework:即通常所說的spring 框架,是一個開源的Java/Java EE全功能棧應用程式框架,其它spring項目如spring boot也依賴於此框架。
  • Vue.Draggable - 基於 Vue.js 絲般柔滑的拖拽排序組件
    在 jQuery 的時代,有很多動效非常流暢絲滑的拖拽排序插件,但在國內 Vue.js 起來後,卻比較難找到一款像 Sortable.js 那樣的組件,而 Vue.Draggable 就是一款基於 Sortable.js 開發的 Vue 增強組件。
  • 1小時學會spring boot,人人都可以成為架構師「手稿」
    之前看到別人一本書,借來觀看,也感謝JZ給我解析一些要點,因為還給別人,所以花了兩天通宵把spring boot這本書看完和做了要點筆記,然後把書還給別人, 因為一邊看書一邊盲寫,字有點醜,會有個別字寫的有點潦草,大家可以聯繫上下文理解,希望不嫌棄。
  • Spring Boot中使用Mockito進行Web測試 - 第339篇
    準備工作2.1 版本說明(1)Spring Boot : 2.4.0(2)Mockito:3.6.0 2.2 新建一個Controller       這裡我們是基於前面的文章       好了進入正題,我們添加web依賴:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId><
  • Spring SPI和Spring Boot SPI - 第345篇
    現在我們大部分的項目都是基於Spring或者Spring Boot開發的,所以這一節我們談談SPI在Spring | Spring Boot中的應用。那麼同樣的道理,spring-boot-autoconfigure模塊也能動態加載了。