第三方登陸--QQ登陸--單體應用
阿新 • • 發佈:2020-08-18
從零玩轉第三方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
對接第三方登入的關鍵就是如何確定使用者是合法登入,如果確定這次登入的和上次登入的是同一個人並且不是假冒的。其實這個並不用我們特別操心,就以微博登入為例,
使用者登入成功之後會回撥一個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登陸