SpringBoot实现QQ第三方登录
一、QQ第三方登录介绍
1、要实现QQ第三方登录必须在QQ互联申请第三方登录接口
2、搞第三方登录,必须先进行接口的申请并得到授权;
3、QQ第三方接口的申请地址:QQ互联 https://connect.qq.com/index.html
4、申请之前,你必须有一个进行过国内备案过的网站,个人也可以;
5、登录QQ互联后,首先填写个人或者公司信息,然后申请网站应用,但是APP的话,需要申请移动应用;
这里有个回调地址注意下,是用户进行第三方登录的时候会带上这个参数一起请求服务器,然后腾讯服务器再调用这个地址,顺便把用户信息带也过来,这样开发者的服务器就能得到用户信息了;
二、实现原理
OAuth 2.0简介
为了简化网站和应用的注册登录开发,第三方登录横空出世,比如QQ登录,微信登录,微博登录等等;
第三方登录也叫做第三方授权,相应的规范是OAuth 2.0;每家提供第三方登录的大型互联网公司都有具体实现,原理一致,大体过程一致,细节有差别;
这里我们先了解下OAuth 2.0原理;
OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。2012年10月,OAuth2.0协议正式发布为RFC 6749 [1]
在认证和授权的过程中涉及的三方包括:
1、服务提供方,用户使用服务提供方来存储受保护的资源,如照片,视频,联系人列表。
2、用户,存放在服务提供方的受保护的资源的拥有者。
3、客户端,要访问服务提供方资源的第三方应用,通常是网站,如提供照片打印服务的网站。在认证过程之前,客户端要向服务提供者申请客户端标识。
使用OAuth进行认证和授权的过程如下所示:
用户想操作存放在服务提供方的资源。
用户登录客户端向服务提供方请求一个临时令牌。
服务提供方验证客户端的身份后,授予一个临时令牌。
客户端获得临时令牌后,将用户引导至服务提供方的授权页面请求用户授权。在这个过程中将临时令牌和客户端的回调连接发送给服务提供方。
用户在服务提供方的网页上输入用户名和密码,然后授权该客户端访问所请求的资源。
授权成功后,服务提供方引导用户返回客户端的网页。
客户端根据临时令牌从服务提供方那里获取访问令牌。
服务提供方根据临时令牌和用户的授权情况授予客户端访问令牌。
客户端使用获取的访问令牌访问存放在服务提供方上的受保护的资源。
QQ第三方登录实现大体过程:
Step1:接入申请,获取appid和apikey;
Step2:放置QQ登录按钮;
Step3:通过用户登录验证和授权,获取Access Token;
Step4:通过Access Token获取用户的OpenID;
Step5:调用OpenAPI,来请求访问或修改用户授权的资源。
三、接口信息
get_user_info接口窥探
申请通过过,腾讯开放平台给我们提供了get_user_info接口
我们得熟悉下这个接口,通过这个接口我们能获取到一些比如昵称,性别,头像等信息;
接口概要
url | https://graph.qq.com/user/get_user_info |
---|---|
支持验证方式 | oauth2.0 |
格式 | JSON |
http请求方式 | GET |
是否需要鉴权 | 需要 |
请求参数:
参数 | 含义 |
---|---|
access_token | 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。 |
oauth_consumer_key | 申请QQ登录成功后,分配给应用的appid,openid 用户的ID,与QQ号码一一对应。 |
可通过调用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 来获取。 |
请求Demo:
https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID
返回Demo:
{ “ret”:0, “msg”:"", “nickname”:“YOUR_NICK_NAME”, … }
返回参数说明:
参数说明 | 描述 |
---|---|
ret | 返回码 |
msg | 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。 |
nickname | 用户在QQ空间的昵称。 |
figureurl | 大小为30×30像素的QQ空间头像URL。 |
figureurl_1 | 大小为50×50像素的QQ空间头像URL。 |
figureurl_2 | 大小为100×100像素的QQ空间头像URL。 |
figureurl_qq_1 | 大小为40×40像素的QQ头像URL。 |
figureurl_qq_2 | 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。 |
gender | 性别。 如果获取不到则默认返回"男" |
三、SpringBoot代码实现
QQ第三方登录官方提供了一个sdk for java
我们可以将其导入到本地仓库中使用
jar、源码获取地址:微信公众号搜索清峰小栈
回复QQ第三方登录
关键的三步:
第一步: 获取腾讯服务器返回的accessToken;
第二步:通过accessToken获取openId;(底层分析,这步请求的是https://graph.qq.com/oauth2.0/me
接口 );
第三步:通过accessToken和openId得到用户信息;(底层分析,这步请求是https://graph.qq.com/user/get_user_info接口)
然后得到数据后 放session,放数据库 以及判断啥的 根据自己的业务来搞即可;
这里的openId是每个用户的唯一标识,所以肯定得处理好;
1、Sdk4J.jar安装到本地maven仓库
maven仓库没有提供下载坐标所以我们这里首先把Sdk4J.jar搞到本地仓库去;
为了方便,我们把Sdk4J.jar放到C:\Users\Administrator目录下;
然后cmd进入命令行
执行 mvn install:install-file -DgroupId=com.qq -DartifactId=Sdk4J -Dversion=2 -Dpackaging=jar -Dfile=Sdk4J.jar
即可;执行完,本地仓库就多了一个jar包;
这样才算是安装到本地仓库;
2、然后我们maven pom.xml配置导入依赖:
<!-- 第三方QQ登录 -->
<dependency>
<groupId>com.qq</groupId>
<artifactId>Sdk4J</artifactId>
<version>2</version>
</dependency>
3、application.yml配置和配置文件:
server:
port: 80
servlet:
context-path: /
tomcat:
uri-encoding: utf-8
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_sss?serverTimezone=GMT
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
thymeleaf:
cache: false
配置文件:
4、index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>QQ登录测试</title>
</head>
<body>
<div style="padding: 40px">
<font color="black"><strong>QQ一键登录测试</strong>
//前端只需提供一个登录QQ的请求即可
<a href="/qqLogin"><img src="/images/qqlogin.png"/></a>
</html>
5、IndexController类:
/* * @author: 清峰
* @date: 2020/11/28 22:43
* @code: 愿世间永无Bug!
* @description: qq登录控制器
*/
@Controller
public class QQController {
@Autowired
private QQUserService qqUserService;
/**
* 根目录请求
*
* @return
*//*
@RequestMapping("/")
public ModelAndView root() {
ModelAndView mav = new ModelAndView();
mav.setViewName("index");
mav.addObject("title", "QQ登录测试");
return mav;
}*/
/**
* qq登录 带参数发送服务器
*
* @param request
* @param response
* @throws Exception
*/
@RequestMapping("/qqLogin")
public void qqLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/html;charset=utf-8");
try {
//底层是重定向到qq的第三方登录界面 其实重定向请求的是
//https://graph.qq.com/oauth2.0/authorize 接口;以及封装一堆参数;
response.sendRedirect(new Oauth().getAuthorizeURL(request));
} catch (QQConnectException e) {
e.printStackTrace();
}
}
/**
* 回调
*
* @param request
* @param response
* @throws Exception
*/
@RequestMapping("/connect")
public String connect(HttpServletRequest request,
HttpServletResponse response,
RedirectAttributes attributes) throws Exception {
response.setContentType("text/html; charset=utf-8");
try {
//根据登录的状态获取AccessToken
AccessToken accessTokenObj = (new Oauth()).getAccessTokenByRequest(request);
String accessToken = null,
openID = null;
long tokenExpireIn = 0L;
if (accessTokenObj.getAccessToken().equals("")) {
// 我们的网站被CSRF攻击了或者用户取消了授权
// 做一些数据统计工作
System.out.print("没有获取到响应参数");
} else {
accessToken = accessTokenObj.getAccessToken(); //授权令牌
tokenExpireIn = accessTokenObj.getExpireIn(); //过期时间
// 利用获取到的accessToken 去获取当前用的openid -------- start
//通过accessToken获取openId;(底层分析,这步请求的是https://graph.qq.com/oauth2.0/me 接口 );
//然后得到数据后 放session,放数据库 以及判断啥的 根据自己的业务来搞即可;
//这里的openId是每个用户的唯一标识,所以肯定得处理好;
OpenID openIDObj = new OpenID(accessToken);
openID = openIDObj.getUserOpenID();
//过accessToken和openId得到用户信息;(底层分析,这步请求是https://graph.qq.com/user/get_user_info接口)
UserInfo qzoneUserInfo = new UserInfo(accessToken, openID);
UserInfoBean userInfoBean = qzoneUserInfo.getUserInfo();
System.out.println("用户信息: " + userInfoBean);
if (userInfoBean.getRet() == 0) { //getRet() 返回状态码 0成功 0<失败
/*用户昵称可能带有表情解决方法参考:
* 修改数据库编码格式 :utf8改成了“utf8mb4”
将昵称进行Base64加密存储,解密输出
存储昵称时进行昵称判断编写过滤emoji工具
引入第三方依赖包:emoji-java
* */
//获取QQ昵称
String nickname = userInfoBean.getNickname(); //用户昵称可能带有表情,存不进数据库,对其进行转码
//获取QQ性别
String gender = userInfoBean.getGender();
//获取QQ头像
String avatar = userInfoBean.getAvatar().getAvatarURL30();
//这里可以做自己的业务逻辑如:将用户信息保存在数据库中
/* request.getSession().setAttribute("demo_access_token", accessToken);
request.getSession().setAttribute("demo_token_expirein", String.valueOf(tokenExpireIn));
request.getSession().setAttribute("demo_openid", openID);*/
} else {
System.out.println("很抱歉,我们没能正确获取到您的信息,原因是: " + userInfoBean.getMsg());
}
}
} catch (QQConnectException e) {
}
return "";
}
}