第三方登陸--QQ登陸--前后端分離版本


從零玩轉第三方QQ登陸

下面有源碼

第三方GITEE登陸

https://www.cnblogs.com/Yangbuyi/p/yangbuyi.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.前台准備


	/**
	 * 封裝一個居中打開新窗口的方法
	 */
	function openWindow(url, width, height) {
		width = width || 600;
		height = height || 400;
		var left = (window.screen.width - width) / 2;
		var top = (window.screen.height - height) / 2;
		var win =window.open(url, "_blank",
			"toolbar=yes, location=yes, directories=no, status=no, menubar=yes, scrollbars=yes, resizable=no, copyhistory=yes, left=" +
			left + ", top=" + top + ", width=" + width + ", height=" + height);
	console.log(win)
	}

3.1 使用 隨便點擊按鈕進行調用這個 qq函數

3.2 點擊訪問后端 登陸方法

4. 后端實現

package top.yangbuyi.system.controller;

import com.google.gson.Gson;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.yangbuyi.system.common.ActiveUser;
import top.yangbuyi.system.common.Constant;
import top.yangbuyi.system.common.HttpsUtils;
import top.yangbuyi.system.common.WebUtils;
import top.yangbuyi.system.config.QQ.OAuthProperties;
import top.yangbuyi.system.config.QQ.vo.QQDTO;
import top.yangbuyi.system.config.QQ.vo.QQOpenidDTO;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * description:  楊不易網站 :www.yangbuyi.top
 * program:  yangbuyi-erp-2020
 * ClassName:  QQController
 * create:  2020-06-24 17:17
 *
 * @author: yangbuyi
 * @since: JDK1.8
 **/

@RestController
@RequestMapping("api")
public class QQController {
      
      @Autowired
      private OAuthProperties oauth;
      private static final Logger logger = LoggerFactory.getLogger(QQController.class);
      
      /**
       * 調用QQ登陸接口
       *
       * @param response
       */
      @GetMapping("/login/oauth")
      public String loginQQ(HttpServletResponse response) {
            /**
             * 重定向
             */
//                  response.sendRedirect();//授權模式,授權碼模式
            
            System.out.println(
                  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"
            );

            return 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";

            
      }
      
      
      //接收回調地址帶過來的code碼
      @GetMapping("/oauth2")
      public String authorizeQQ(Map
 
 
 
         
          
  
  
   msg, String code, HttpServletResponse response) {
            HashMap
  
  
  
          
            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=9724892714FDF1E3ED5A4C6D074AF9CB&expires_in=7776000&refresh_token=9E0DE422742ACCAB629A54B3BFEC61FF String result = HttpsUtils.doGet(oauth.getQQ().getAccess_token_callback_uri(), params); //對拿到的數據進行切割字符串 String[] strings = result.split("&"); //切割好后放進map Map 
           
             reulsts = new HashMap<>(); for (String str : strings) { String[] split = str.split("="); if (split.length > 1) { reulsts.put(split[0], split[1]); } } //到這里access_token已經處理好了 //下一步獲取openid,只有拿到openid才能拿到用戶信息 String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + "?access_token=" + reulsts.get("access_token")); //接下來對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作為密碼(正常情況下,在開發時候用openid作為用戶名,再自己定義個密碼就可以了) try { System.out.println("用戶信息:" + userInfo); System.out.println(qqDTO); // 獲取主體 Subject subject = SecurityUtils.getSubject(); // SecurityUtils.getSubject().login(new UsernamePasswordToken(qqOpenidDTO.getOpenid(), Constant.DEFAULT_PWD)); System.out.println(qqOpenidDTO.getOpenid()); subject.login(new UsernamePasswordToken(qqDTO.getNickname(), Constant.DEFAULT_PWD)); String token = subject.getSession().getId().toString(); ActiveUser active = (ActiveUser) subject.getPrincipal(); params.put("token", token); params.put("code", 200); params.put("permissions", active.getPermissions()); params.put("username", active.getUser().getName()); params.put("usertype", active.getUser().getType()); System.out.println("Shiro認證成功"); } catch (Exception e) { msg.put("msg", "第三方登陸失敗,請聯系管理!"); logger.error(e.getMessage()); System.out.println("Shiro認證失敗"); // return new ResultObj(-1, "login.html"); // return "redirect:https://www.yangbuyi.top/login.html"; WebUtils.getHttpSession().setAttribute("params", params); return "/login.html"; } // return "redirect:" + "https://www.yangbuyi.top/" // params WebUtils.getHttpSession().setAttribute("params", params); // return "redirect:https://www.yangbuyi.top/index.html"; // return "getParams"; return "/index.html"; } /** * 獲取參數 * * @return */ @RequestMapping("getParams") public Object getParams() { System.out.println(WebUtils.getHttpSession().getAttribute("params")); return WebUtils.getHttpSession().getAttribute("params"); } } 
            
          
 
 
 
         

項目源代碼--- 請移步GITee

https://gitee.com/yangbuyi/yby_qq


免責聲明!

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



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