第三方登录--QQ登录--单体应用
从零玩转第三方QQ登录
下面有源码
前后端分离版本
一样的思路
https://www.cnblogs.com/Yangbuyi/p/13194007.html
第三方GITEE登录
https://www.cnblogs.com/Yangbuyi/p/yangbuyi.html
oss对象存储超好玩
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登陆
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南