第三方登陆--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
第三方登陆--QQ登陆--前后端分离版本的更多相关文章
- 第三方登陆--QQ登陆--单体应用
从零玩转第三方QQ登陆 下面有源码 前后端分离版本 一样的思路 https://www.cnblogs.com/Yangbuyi/p/13194007.html 第三方GITEE登陆 https:// ...
- spring boot 2.0.0 + shiro + redis实现前后端分离的项目
简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大 ...
- laravel前后端分离的用户登陆 退出 中间件的接口与session的使用
在项目开发的过程中,需要有用户的登陆 退出 还有校验用户是否登陆的中间件; 基本思路: 登陆: 前端请求接口的参数校验 用户名 密码规则的校验 用户名密码是否正确的校验; 如果上面的校验都通过的了,把 ...
- Jeecg-Boot 2.0 版本发布,基于Springboot+Vue 前后端分离快速开发平台
目录 Jeecg-Boot项目简介 源码下载 升级日志 Issues解决 v1.1升级到v2.0不兼容地方 系统截图 Jeecg-Boot项目简介 Jeecg-boot 是一款基于代码生成器的智能开发 ...
- JEECG前后端分离UI框架实战版本抢先体验(ng2-admin+Angular4+AdminLTE+WebStorm)
JEECG前后端分离UI框架实战版本 - 抢先体验 (ng2-admin+Angular4+AdminLTE) 关键词: ng2-admin.Angular4.AdminLTE.Nodejs.Jeec ...
- 「newbee-mall新蜂商城开源啦」 前后端分离的 Vue 版本即将开源
新蜂商城 Vue 版本 2019 年 10 月份我在 GitHub 开源仓库中上传了新蜂商城项目的所有源码,至今已经有小半年的时间了,感兴趣的可以去了解一下这个 Spring Boot 技术栈开发的商 ...
- 前后端分离ueditor富文本编辑器的使用-Java版本
最近在写一个自己的后台管理系统(主要是写着玩的,用来熟悉后端java的知识,目前只是会简单的写点接口),想在项目中编写一个发布新闻文章的功能,想到了使用百度的ueditor富文本编辑器,网上找了很多j ...
- 前后端分离djangorestframework—— 在线视频平台接入第三方加密防盗录视频
加密视频 在以后的开发项目中,很可能有做在线视频的,而在线视频就有个问题,因为在线播放,就很有可能视频数据被抓包,如果这个在线视频平台有付费视频的话,这样就会有人做点倒卖视频的生意了,针对这个问题,目 ...
- Jeecg-Boot 2.0.1 版本发布,前后端分离快速开发平台
Jeecg-Boot项目简介 Jeecg-boot 是一款基于代码生成器的快速开发平台! 采用前后端分离技术:SpringBoot,Mybatis,Shiro,JWT,Vue & Ant De ...
- 运用NodeJs环境并依赖第三方库,框架等实现网站前后端分离报错问题及处理方法
运用NodeJs环境并依赖第三方库,框架等实现网站前后端分离报错问题及处理方法 问题一: SyntaxError: missing ) after argument list in .....\vie ...
随机推荐
- 小知识:Docker环境缺少vi命令,如何解决
docker exec可以使用--user参数指定root用户,进入安装vi即可: [opc@oci-001 ~]$ docker exec -it --user root testdb bash b ...
- 「codeforces - 1394C」Boboniu and String
link. 注意到 BN-string 长成什么样根本不重要,我们把它表述为 BN-pair \((x, y)\) 即可,两个 BN-strings 相似的充要条件即两者分别映射得到的 BN-pair ...
- 汇编debug的安装
实验一查看CPU和内存,用机器指令和汇编指令编程 在做实验前需要debug命令. 工具:dosbox,debug.exe 安装:dosbox :https://www.dosbox.com/ debu ...
- TOP GP 把已经编译的per反编回对应版本的4fd(画面档)
由于GP5.1,5.2,5.3的genero对应版本画面档开发互不兼容,下面提供各版本之间互转的操作方法: xshell切换到要反编译的per档目录,执行以下命令,就会在同目录下生成对应4fd档资料 ...
- 【RocketMQ】RocketMQ 5.0新特性(二)- Pop消费模式
Pop模式消费和消息粒度负载均衡 在RocketMQ 5.0之前,消费有两种方式可以从Broker获取消息,分别为Pull模式和Push模式. Pull模式:消费需要不断的从阻塞队列中获取数据,如果没 ...
- 关于fdisk -l
摘取:https://blog.csdn.net/digitalkee/article/details/104226846 # fdisk -l Disk /dev/hda: 160.0 GB, 16 ...
- CF862B
题目简化和分析: 这是一道较为经典的二分图染色题. 二分图的基本概念 但这题让我们求得是完全二分图. 什么是完全二分图 \(cnt_{1}\) 表示染成颜色种类为 \(1\) 的个数. \(cnt_{ ...
- LibOciLib使用说明(2017-1-26更新)
LibOciLib使用说明 整理者:赤勇玄心行天道 QQ:280604597 Email:280604597@qq.com 大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的! ...
- JDK 动态代理原理
代理模式 客户端不直接访问目标对象,需要通过第三者来实现间接访问对象 代理对象在客户端和目标对象之间起中介作用,能够屏蔽目标对象不想让客户端知道的内容,或增加额外的服务 动态代理 JDK 动态代理:基 ...
- vivo 网络端口安全建设技术实践
作者:vivo 互联网安全团队 - Peng Qiankun 随着互联网业务的快速发展,网络攻击的频率和威胁性也在不断增加,端口是应用通信中的门户,它是数据进出应用的必经之路,因此端口安全也逐渐成为了 ...