1. 程式人生 > 實用技巧 >第三方登陸--QQ登陸--單體應用

第三方登陸--QQ登陸--單體應用

從零玩轉第三方QQ登陸

下面有原始碼

前後端分離版本

一樣的思路

https://www.cnblogs.com/Yangbuyi/p/13194007.html

第三方GITEE登陸

https://www.cnblogs.com/Yangbuyi/p/yangbuyi.html

oss物件儲存chao'hao'w

https://www.cnblogs.com/Yangbuyi/p/13493845.html

在真正開始對接之前,我們先來聊一聊後臺的方案設計。既然是對接第三方登入,那就免不了如何將使用者資訊儲存。首先需要明確一點的是,使用者在第三方登入成功之後,
我們能拿到的僅僅是一個代表使用者唯一身份的ID(微博是真實uid

,QQ是加密的openID)以及用來識別身份的accessToken,當然還有暱稱、頭像、性別等有限資料,
對接第三方登入的關鍵就是如何確定使用者是合法登入,如果確定這次登入的和上次登入的是同一個人並且不是假冒的。其實這個並不用我們特別操心,就以微博登入為例,
使用者登入成功之後會回撥一個code給我們,然後我們再拿code去微博那換取 accessToken ,如果這個code是使用者亂填的,那這一關肯定過不了,所以,前面的擔心有點多餘,哈哈。

1. 認識Oauth2.0

現在很多網站都要不管是為了引流也好,為了使用者方便也好一般都有第三方賬號登陸的需求,今天以QQ登陸為例,來實現一個最簡單的第三方登陸。
目前主流的第三方登入都是依賴的Oauth2.0實現的,最常見的就是在各種中小型網站或者App中的QQ登入,微信登入等等。所以我建議想要學習和實現第三方登入同學去了解下這個協議。

1.2 必須要域名並且進行備案

比如我的域名: https://yangbuyi.top/
因為騰訊有一個域名認證機制啥的。。。。。。

2.實名認證

QQ登入我們對接的是QQ互聯,地址:https://connect.qq.com ,首先需要註冊成為開發者並實名認證,需要手持身份證照片,具體就不講了。

2.1、進行申請開發者身份

2.2 建立應用

進入應用管理頁面建立應用,根據實際需要是建立網站應用還是移動應用,我這裡是網站應用:

提交成功完步後等待客服稽核即可

2.3. QQ登陸流程

2.4. 請求引數

3.前臺準備

建立 home.html login.html


前端程式碼

## login.html

  <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="/api/login/oauth">
    <input type="submit" value="登陸">
</form>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="">
    <label class="">登陸成功</label>
    <div class="">
        <p th:text="'openID :' + ${map.qqOpenidDTO.openid}"></p>

        <p th:text="'使用者名稱稱 :' + ${map.user.nickname}"></p>

        使用者頭像:
        <img th:src="${map.user.figureurl_qq_1}" alt="">
        <br>
        <img th:src="${map.user.figureurl_qq_1}" alt="">
        <img th:src="${map.user.figureurl_qq_2}" alt="">
ht

    </div>
</div>

</body>
</html>		

4. 後端實現

## pom依賴匯入

  <!-- qq登陸整合 開始 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpasyncclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
        </dependency>
        <!--json轉換工具-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <!--QQSDK-->
        <dependency>
            <groupId>net.gplatform</groupId>
            <artifactId>Sdk4J</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <!-- qq登陸整合 結束 -->


        <!-- 模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

yml配置檔案

server:
  port: 8080



# QQ 登入 引數
oauth:
  qq:
    #你的appid
    client_id: ????????
    #你的appkey
    client_secret: ????????
    #你接收響應code碼地址
    redirect_uri: ????????
    #騰訊獲取code碼地址
    code_callback_uri: ????????
    #騰訊獲取access_token地址
    access_token_callback_uri: ????????
    #騰訊獲取openid地址
    openid_callback_uri: ????????
    #騰訊獲取使用者資訊地址
    user_info_callback_uri: ????????

spring:
  thymeleaf:
    cache: false
    suffix: .html

建立HttpsUtils

用於傳送QQ伺服器請求----請在gitee獲取

建立QQ包當中的所有檔案----請在gitee獲取

QQDTO--- 用於儲存QQ伺服器返回來的引數

QQOpenidDTO--- 用來儲存 access_token、openid

OAuthProperties--- 用於成為配置檔案yml注入引數到QQProperties獲取code碼

QQProperties--- 用於儲存訪問QQ伺服器必要的引數

5. 建立路由跳轉

RequestController

@Controller
@Slf4j
public class RequestController {
	 // 登陸
     @RequestMapping("login")
     public String login() {
         return "login";
     }
    
    
	  // 登陸成功跳轉
     @RequestMapping("home")
     public String home() {
         return "home";
     }

}

6.建立LoginController

package top.yangbuyi.controller;


import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.yangbuyi.QQ.OAuthProperties;
import top.yangbuyi.QQ.vo.QQDTO;
import top.yangbuyi.QQ.vo.QQOpenidDTO;
import top.yangbuyi.common.HttpsUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @description: 楊不易網站:www.yangbuyi.top
 * @program: qqlogindemo
 * @ClassName: loginController
 * @create: 2020-08-18 14:41
 * @author: yangbuyi
 * @since: JDK1.8
 * @loginController:  第三方QQ登陸
 **/

@Controller
@Slf4j
@RequestMapping("api")
public class loginController {

     /**
      * 認證引數
      */
     @Autowired
     private OAuthProperties oauth;


     /**
      * 呼叫QQ登陸介面
      *
      * @param response
      */
     @GetMapping("/login/oauth")
     public void loginQQ(HttpServletResponse response) {
         /**
          * 重定向
          */
         try {
              response.sendRedirect(oauth.getQQ().getCode_callback_uri() + //獲取code碼地址
                    "?client_id=" + oauth.getQQ().getClient_id()//appid
                    + "&state=" + UUID.randomUUID() + //這個說是防攻擊的,就給個隨機uuid吧
                    "&redirect_uri=" + oauth.getQQ().getRedirect_uri() +//這個很重要,這個是回撥地址,即就收騰訊返回的code碼
                    "&response_type=code");
         } catch (IOException e) {
              e.printStackTrace();
         }
     }

 	 /**
	   * 接收回調地址帶過來的code碼
	   * @param code
	   * @param request
	   * @return
	   */
     @GetMapping("/oauth2")
     public String authorizeQQ(String code, HttpServletRequest request) {
         HashMap<String, Object> params = new HashMap<>();
         params.put("code", code);
         params.put("grant_type", "authorization_code");
         params.put("redirect_uri", oauth.getQQ().getRedirect_uri());
         params.put("client_id", oauth.getQQ().getClient_id());
         params.put("client_secret", oauth.getQQ().getClient_secret());

         // 獲取騰訊access token
         /**
         遍歷拿到的資料:
            access_token=6D45343586C39EAA1CFF016E081E4F3E
            refresh_token=3CD8AD92C146A6154AF89DD1DEEA86BB
            expires_in=7776000
         */
         Map<String, String> reulsts = getAccess_token(params);
       
         

         //到這裡access_token已經處理好了
         //下一步獲取openid,只有拿到openid才能拿到使用者資訊
         String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + "?access_token=" + reulsts.get("access_token"));

         System.out.println("openidContent: " + openidContent);

         //接下來對openid進行處理
         //擷取需要的那部分json字串
         String openid = openidContent.substring(openidContent.indexOf("{"), openidContent.indexOf("}") + 1);
         Gson gson = new Gson();
         //將返回的openid轉換成DTO
         QQOpenidDTO qqOpenidDTO = gson.fromJson(openid, QQOpenidDTO.class);

         //接下來說說獲取使用者資訊部分
         //登陸的時候去資料庫查詢使用者資料對於openid是存在,如果存在的話,就不用拿openid獲取使用者資訊了,而是直接從資料庫拿使用者資料直接認證使用者,
         // 否則就拿openid去騰訊伺服器獲取使用者資訊,並存入資料庫,再去認證使用者
         //下面關於怎麼獲取使用者資訊,並登陸
         params.clear();
         params.put("access_token", reulsts.get("access_token"));//設定access_token
         params.put("openid", qqOpenidDTO.getOpenid());//設定openid
         params.put("oauth_consumer_key", qqOpenidDTO.getClient_id());//設定appid
         //獲取使用者資訊
         String userInfo = HttpsUtils.doGet(oauth.getQQ().getUser_info_callback_uri(), params);
         QQDTO qqDTO = gson.fromJson(userInfo, QQDTO.class);
         // (正常情況下,在開發時候用openid作為使用者名稱,再自己定義個密碼就可以了)
         try {

              /* 組裝資料 */
              HashMap<String, Object> map = new HashMap<>();
              map.put("user", qqDTO);
              map.put("qqOpenidDTO", qqOpenidDTO);
              request.setAttribute("map", map);
              log.info("user資料:{}" + qqDTO);
              log.info("qqOpenidDTO資料:{}" + qqOpenidDTO);
              return "home";
         } catch (Exception e) {
              e.printStackTrace();
              return "login";
         }
     }


     /**
      * 獲取騰訊 access_token
      *
      * @return
      */
     public Map<String, String> getAccess_token(HashMap<String, Object> params) {
         // 認證地址
         //獲取access_token如:access_token=9724892714FDF1E3ED5A4C6D074AF9CB&expires_in=7776000&refresh_token=9E0DE422742ACCAB629A54B3BFEC61FF
         String result = HttpsUtils.doGet(oauth.getQQ().getAccess_token_callback_uri(), params);
         //對拿到的資料進行切割字串
         String[] strings = result.split("&");
         //切割好後放進map
         Map<String, String> reulsts = new HashMap<>();
         for (String str : strings) {
              String[] split = str.split("=");
              if (split.length > 1) {
                  reulsts.put(split[0], split[1]);
              }
         }
         return reulsts;
     }


}

7. 測試QQ登陸



專案原始碼--- 請移步GITee

https://gitee.com/yangbuyi/bky_yby