QQ第三方授权登录OAuth2.0实现(Java)
准备材料
- 1.已经备案好的域名
- 2.服务器(域名和服务器为统一主体或域名已接入服务器)
- 3.QQ号
- 4.开发流程:https://wiki.connect.qq.com/准备工作_oauth2-0
创建应用
- 1.访问 https://connect.qq.com/manage.html ,登录。
- 2.创建网站应用,填写网站基本信息以及平台信息,提交审核。注:网站回调域后续会用到,是点击授权登录时回调地址,需要与后续开发一致。
程序开发
1. 添加QQ登录按钮,用于点击跳转至QQ授权登录页
<a href="/account/qqConnect" class="blog-user"> <i
class="fa fa-qq"></i>
</a>
2. Java后台实现页面跳转
2.1 编写一个工具类
QQUtil
package cn.zwqh.springboot.common.qq;
import java.io.IOException;
import java.net.URLEncoder;
import org.apache.http.client.ClientProtocolException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import cn.zwqh.springboot.common.http.HttpClientUtils;
import cn.zwqh.springboot.entity.sys.User;
public class QQUtil {
private static final Logger log = LoggerFactory.getLogger(QQUtil.class);
private static final String QQ_APP_ID="XXX";//改成自己的
private static final String QQ_APP_SECRET="XXX";//改成自己的
private static final String LOGIN_REDIRECT_URI="https://www.zwqh.top/account/qqLogin"; //改成自己的
private static final String BIND_REDIRECT_URI="https://www.zwqh.top/account/qqBind"; //改成自己的
private static final String AUTH_CODE_URL="https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id="+QQ_APP_ID+"&redirect_uri=REDIRECT_URI&state=STATE";
private static final String ACCESS_TOKEN_URL="https://graph.qq.com/oauth2.0/token?client_id="+QQ_APP_ID+"&client_secret="+QQ_APP_SECRET+"&code=CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI";
private static final String REFRESH_TOKEN_URL="https://graph.qq.com/oauth2.0/token?client_id="+QQ_APP_ID+"&client_secret="+QQ_APP_SECRET+"&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";
private static final String OPEN_ID_URL="https://graph.qq.com/oauth2.0/me?access_token=ACCESS_TOKEN";
private static final String USER_INFO_URL="https://graph.qq.com/user/get_user_info?access_token=ACCESS_TOKEN&oauth_consumer_key="+QQ_APP_ID+"&openid=OPENID";
public static JSONObject getJsonStrByQueryUrl(String paramStr){
String[] params = paramStr.split("&");
JSONObject obj = new JSONObject();
for (int i = 0; i < params.length; i++) {
String[] param = params[i].split("=");
if (param.length >= 2) {
String key = param[0];
String value = param[1];
for (int j = 2; j < param.length; j++) {
value += "=" + param[j];
}
try {
obj.put(key,value);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
return obj;
}
/**
* 获取授权登录页码url
* @return
*/
public static String getLoginConnectUrl(String state) {
String url=null;
try{
url=AUTH_CODE_URL.replace("REDIRECT_URI", URLEncoder.encode(LOGIN_REDIRECT_URI, "utf-8")).replace("STATE", state);
}catch (Exception e) {
log.error(e.toString());
}
return url;
}
/**
* 获取授权绑定页码url
* @return
*/
public static String getBindConnectUrl() {
String url=null;
try{
url=AUTH_CODE_URL.replace("REDIRECT_URI", URLEncoder.encode(BIND_REDIRECT_URI, "utf-8"));
}catch (Exception e) {
log.error(e.toString());
}
return url;
}
/**
* 获取AccessToken
* @return 返回拿到的access_token及有效期
*/
public static QQAccessToken getQQLoginAccessToken(String code) throws ClientProtocolException, IOException{
QQAccessToken token = new QQAccessToken();
String url = ACCESS_TOKEN_URL.replace("CODE", code).replace("REDIRECT_URI", URLEncoder.encode(LOGIN_REDIRECT_URI, "utf-8"));
log.info("这是请求路径:"+url);
String result = HttpClientUtils.doGet(url);
JSONObject jsonObject=getJsonStrByQueryUrl(result);
log.info("这是返回结果:"+jsonObject);
if(jsonObject!=null){ //如果返回不为空,将返回结果封装进AccessToken实体类
token.setAccessToken(jsonObject.getString("access_token"));//接口调用凭证
token.setExpiresIn(jsonObject.getInteger("expires_in"));//access_token接口调用凭证超时时间,单位(秒)
token.setRefreshToken(jsonObject.getString("refresh_token"));
}
return token;
}
/**
* 获取AccessToken
* @return 返回拿到的access_token及有效期
*/
public static QQAccessToken getQQBindAccessToken(String code) throws ClientProtocolException, IOException{
QQAccessToken token = new QQAccessToken();
String url = ACCESS_TOKEN_URL.replace("CODE", code).replace("REDIRECT_URI", URLEncoder.encode(BIND_REDIRECT_URI, "utf-8"));
log.info("这是请求路径:"+url);
String result = HttpClientUtils.doGet(url);
JSONObject jsonObject=getJsonStrByQueryUrl(result);
log.info("这是返回结果:"+jsonObject);
if(jsonObject!=null){ //如果返回不为空,将返回结果封装进AccessToken实体类
token.setAccessToken(jsonObject.getString("access_token"));//接口调用凭证
token.setExpiresIn(jsonObject.getInteger("expires_in"));//access_token接口调用凭证超时时间,单位(秒)
token.setRefreshToken(jsonObject.getString("refresh_token"));
}
return token;
}
/**
* 刷新或续期access_token使用
* @return 返回拿到的access_token及有效期
*/
public static QQAccessToken refreshQQAccessToken(String refreshToken) throws ClientProtocolException, IOException{
QQAccessToken token = new QQAccessToken();
String url = REFRESH_TOKEN_URL.replace("REFRESH_TOKEN",refreshToken);
log.info("这是请求路径:"+url);
String result = HttpClientUtils.doGet(url);
log.info("这是返回结果:"+result);
JSONObject jsonObject=getJsonStrByQueryUrl(result);
log.info("这是转为json的结果:"+jsonObject);
if(jsonObject!=null){ //如果返回不为空,将返回结果封装进AccessToken实体类
token.setAccessToken(jsonObject.getString("access_token"));//接口调用凭证
token.setExpiresIn(jsonObject.getInteger("expires_in"));//access_token接口调用凭证超时时间,单位(秒)
token.setRefreshToken(jsonObject.getString("refresh_token"));
}
return token;
}
/**
* 获取QQopenId
* @return QQopenId
*/
public static String getQQOpenId(String accessToken) throws ClientProtocolException, IOException{
String url = OPEN_ID_URL.replace("ACCESS_TOKEN",accessToken);
log.info("这是请求路径:"+url);
String result = HttpClientUtils.doGet(url).replace("callback(", "").replace(");", "");
log.info("这是返回结果:"+result);
JSONObject jsonObject=JSON.parseObject(result);
log.info("这是转为json的结果:"+jsonObject);
if(jsonObject!=null&&jsonObject.getString("openid")!=null){ //如果返回不为空
return jsonObject.getString("openid");
}
return null;
}
/**
* 获取QQ用户信息
* @param accessToken
* @param openId
* @return
* @throws IOException
* @throws ClientProtocolException
*/
public static JSONObject getUserInfo(String accessToken, String openId) throws ClientProtocolException, IOException {
// 拼接请求地址
String url = USER_INFO_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
log.info("这是请求路径:"+url);
String result = HttpClientUtils.doGet(url);
log.info("这是返回结果:"+result);
JSONObject jsonObject=JSONObject.parseObject(result);
log.info("这是转为json的结果:"+jsonObject);
JSONObject json=new JSONObject();
if (jsonObject!=null&&jsonObject.getInteger("ret").equals(0)) {
try {
User user= new User();
// 用户的标识
user.setQqId(openId);
// 昵称
user.setNickname(jsonObject.getString("nickname"));
if(jsonObject.getString("figureurl_2")!=null&&!jsonObject.getString("figureurl_2").isEmpty()) {
// 用户头像
user.setAvatar(jsonObject.getString("figureurl_qq_2"));
}else {
// 用户头像
user.setAvatar(jsonObject.getString("figureurl_qq_1"));
}
json.put("success", true);
json.put("msg", "success");
json.put("user", user);
} catch (Exception e) {
int errorCode = jsonObject.getInteger("ret");
String errorMsg = jsonObject.getString("msg");
log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, e.toString());
json.put("success", false);
json.put("msg", errorMsg);
json.put("user", null);
}
}else {
json.put("success", false);
json.put("msg", "请先登录");
json.put("user", null);
}
return json;
}
}
QQAccessToken
package cn.zwqh.springboot.common.qq;
import java.io.Serializable;
public class QQAccessToken implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5258435811207021018L;
private String accessToken;//接口调用凭证
private int expiresIn;//access_token接口调用凭证超时时间,单位(秒)
private String openid;//授权用户唯一标识
private String refreshToken;//用户刷新access_token
private String scope;//用户授权的作用域,使用逗号(,)分隔
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
2.2 Controller层实现
package cn.zwqh.springboot.action.web;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import cn.zwqh.springboot.action.BaseAction;
import cn.zwqh.springboot.common.CookieUtil;
import cn.zwqh.springboot.common.DateUtil;
import cn.zwqh.springboot.common.EscapeUnescape;
import cn.zwqh.springboot.common.qq.QQAccessToken;
import cn.zwqh.springboot.common.qq.QQUtil;
import cn.zwqh.springboot.common.redis.RedisHandle;
import cn.zwqh.springboot.entity.SessionUser;
import cn.zwqh.springboot.entity.sys.User;
import cn.zwqh.springboot.service.UserService;
@Controller
@RequestMapping("/account")
public class AccountAction extends BaseAction {
/**
*
*/
private static final long serialVersionUID = 1729415442021645693L;
@Resource
private RedisHandle redisHandle;
@Autowired
private UserService userService;
/**
* 跳转至QQ登录界面
*/
@RequestMapping("/qqConnect")
@ResponseBody
public void qqConnect() {
try {
String referer = getRequest().getHeader("REFERER");
String state = DateUtil.formatUserDefineDate(new Date(), "yyyyMMddHHmmssSSS");
redisHandle.set(state, referer, 60 * 30L);
getResponse().sendRedirect(QQUtil.getLoginConnectUrl(state));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* QQ第三方登录
*
* @throws Exception
*/
@RequestMapping("/qqLogin")
@ResponseBody
public void qqLogin() throws Exception {
String code = getRequest().getParameter("code");
String state = getRequest().getParameter("state");
System.out.println("code = " + code + ", state = " + state);
if (code != null && !"".equals(code)) {
QQAccessToken qqAccessToken = QQUtil.getQQLoginAccessToken(code);
if (qqAccessToken.getAccessToken().equals("")) {
// 我们的网站被CSRF攻击了或者用户取消了授权
// 做一些数据统计工作
System.out.print("没有获取到响应参数");
// 跳转返回地址
outJsonFailure("未获取到AccessToken,请重新进行QQ授权登录");
} else {
QQAccessToken qqAccessToken2 = QQUtil.refreshQQAccessToken(qqAccessToken.getRefreshToken());
String accessToken = qqAccessToken2.getAccessToken();
String referer = redisHandle.get(state).toString();
redisHandle.set(accessToken, referer, 60 * 30L);
redisHandle.remove(state);
getResponse().sendRedirect("https://www.zwqh.top/account/getQQUserInfo?qqAccessToken=" + accessToken);
}
} else {
outJsonFailure("缺少code参数");
}
}
/**
* 获取QQ用户信息
*
* @param qqAccessToken
* @throws Exception
*/
@GetMapping("/getQQUserInfo")
public String getQQUserInfo(String qqAccessToken) throws Exception {
System.out.println("accessToken = " + qqAccessToken);
String referer = redisHandle.get(qqAccessToken).toString();
if (qqAccessToken != null && !"".equals(qqAccessToken)) {
try {
String qqOpenId = QQUtil.getQQOpenId(qqAccessToken);
if (qqOpenId != null) {
System.out.println("**************qq登录成功 qqOpenId = " + qqOpenId);
// 获取QQ用户信息
JSONObject object = QQUtil.getUserInfo(qqAccessToken, qqOpenId);
// 数据库中判断qqOpenId是否存在,存在则登录,不存在则注册
User user = userService.getUserByQQOpenId(qqOpenId);
if (user != null) {
user.setAvatar(object.getJSONObject("user").getString("avatar"));
user.setNickname(object.getJSONObject("user").getString("nickname"));
user.setLastLoginTime(DateUtil.formatDateTime(new Date()));
userService.updateUser(user);
SessionUser suser = SessionUser.getInstance(user);
String token = UUID.randomUUID().toString();
redisHandle.set(token, suser, 60 * 60L * 24 * 7);// 设置用户缓存及过期时间(一星期)
JSONObject data = new JSONObject();
data.put("userId", user.getId());
data.put("nickname", user.getNickname());
data.put("avatar", user.getAvatar());
data.put("token", token);
CookieUtil.setValue(getResponse(), "loginUser", data.toString());
} else {
user = new User();
user.setAvatar(object.getJSONObject("user").getString("avatar"));
user.setNickname(object.getJSONObject("user").getString("nickname"));
user.setLastLoginTime(DateUtil.formatDateTime(new Date()));
user.setRegisterTime(DateUtil.formatDateTime(new Date()));
user.setQqId(qqOpenId);
userService.insertUser(user);
SessionUser suser = SessionUser.getInstance(user);
String token = UUID.randomUUID().toString();
redisHandle.set(token, suser, 60 * 60L * 24 * 7);// 设置用户缓存及过期时间(一星期)
JSONObject data = new JSONObject();
data.put("userId", user.getId());
data.put("nickname", user.getNickname());
data.put("avatar", user.getAvatar());
data.put("token", token);
CookieUtil.setValue(getResponse(), "loginUser", data.toString());
}
} else {
putInRequest("error", "未获取到用户openid,请重新QQ授权登录");
}
} catch (Exception e) {
e.printStackTrace();
putInRequest("error", "登录异常");
}
} else {
putInRequest("error", "缺少code参数");
}
return "redirect:" + referer;
}
/**
* 退出登录
* @return
*/
@RequestMapping("/logout")
public String logout() {
String referer = getRequest().getHeader("REFERER");
String data= CookieUtil.getCookieValue(getRequest(), "loginUser");
if(data!=null&&data!="") {
JSONObject user=JSONObject.parseObject(EscapeUnescape.unescape(data));
String token=user.getString("token");
redisHandle.remove(token);
CookieUtil.deleteValue("loginUser",getResponse());
}
return "redirect:" + referer;
}
}
2.3 JavaScript 处理页面
var data=eval('('+unescape(getCookie("loginUser"))+')');
var a = document.getElementsByClassName("blog-user")[0];
if(data!=null){
a.setAttribute("href","/account/logout");
a.innerHTML='<img alt="'+data.nickname+'" title="'+data.nickname+'" src="'+data.avatar+'" class="layui-circle" width="40px" height="40px">';
}else{
a.setAttribute("href","/account/qqConnect");
a.innerHTML='<i class="fa fa-qq"></i>';
}
总结
总的来说QQ授权登录还是很简单的,该方法使用web端以及wap端。
非特殊说明,本文版权归 朝雾轻寒 所有,转载请注明出处.
本文标题: QQ第三方授权登录OAuth2.0实现(Java)
本文网址:https://www.zwqh.top/article/info/7
如果文章对您有帮助,请扫码关注下我的公众号,文章持续更新中...

QQ第三方授权登录OAuth2.0实现(Java)的更多相关文章
- .NET Core+QQ第三方授权登录
安装包 dotnet add package AspNet.Security.OAuth.QQ 接上文GitHub第三方授权登录 申请过程不介绍了,申请者资料,个人也是可以申请成功的. 这时候有二个参 ...
- C# winform C/S WebBrowser qq第三方授权登录
qq的授权登录,跟微信相似,不同的地方是: 1 申请appid与appkey的时候,注意填写回调地址. 2 这里可以在WebBrowser的是Navigated事件中直接得到Access Token, ...
- 【第二十一篇】手C# MVC 微信授权登录 OAuth2.0授权登录
首先一定要熟读,最起码过一遍微信开发者文档 微信开发者文档 文档写的很清楚 授权登录四步走 在正文开始前,我得讲清楚一个事情 敲黑板,划重点:微信一共有两个 access_token 一个是7200就 ...
- 什么是“QQ登录OAuth2.0”
1. 什么是“QQ登录OAuth2.0 OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他 ...
- 【转】【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
转载请注明出处:http://blog.csdn.net/yangyu20121224/article/details/9057257 由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字 ...
- 【Android应用开发详解】实现第三方授权登录、分享以及获取用户资料
由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折,查阅了一番资料,做了一个Demo.实现起来的效果还是不错的,不敢独享,决定写一个总结的教程,供大家互相 ...
- 使用ShareSDK实现第三方授权登录、分享以及获取用户资料效果,项目中包含:源码+效果图+项目结构图
[Android应用开发详解]第01期:第三方授权认证(一)实现第三方授权登录.分享以及获取用户资料 由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折, ...
- SpringBoot基于JustAuth实现第三方授权登录
1. 简介 随着科技时代日渐繁荣,越来越多的应用融入我们的生活.不同的应用系统不同的用户密码,造成了极差的用户体验.要是能使用常见的应用账号实现全应用的认证登录,将会更加促进应用产品的推广,为生活 ...
- github 授权登录教程与如何设计第三方授权登录的用户表
需求:在网站上想评论一篇文章,而评论文章是要用户注册与登录的,那么怎么免去这麻烦的步骤呢?答案是通过第三方授权登录.本文讲解的就是 github 授权登录的教程. 效果体验地址:http://biao ...
随机推荐
- c语言实现双色球和大乐透
头文件: #include<stdio.h> #include <stdlib.h> #include<string.h> #include <time.h& ...
- Python入门基础:七段数码管绘制
1.在学习Python的过程中,运用所学的一些基础知识,进行一些简单的编程,可以收获很多乐趣.在生活中,LED灯无处不在,荧幕显示的广告词,给我们呈现出动态的视觉效果.下面,则以最简单的显示日期为例, ...
- HashMap面试必问的数据结构相关知识
如果在看这篇文章时,对HashMap的结构还不是很了解,建议你参考前段时间写的<刨死你系列——HashMap剖析(基于jdk1.8)>,可能会对下面的提及到知识点有些帮助. 1:HashM ...
- JVM学习(虚拟机栈、堆、方法区)自我看法
堆(Heap): 此内存区域唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配.这一点在java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配. 虚拟机栈(Stack): 虚拟机栈主 ...
- @classmethod @staticmethod 个人理解
官方解释 @classmethod 一个类方法把类自己作为第一个实参, 就像一个实例方法把实例自己作为第一个实参. 语法格式: class C: @classmethod def f(cls, arg ...
- Servlet重定向
重定向就是在访问一个Servlet时,没有得到资源,只能去找另外一个Servlet去获取资源.状态码为302. import javax.servlet.ServletException; impor ...
- Spring+Mybatis整合的练手小项目(一)项目部署
声明:教程是网上找的,代码是自己敲的 项目目录大致如下: 1. 首先创建Maven工程,在pom.xml中加入项目所需依赖: <?xml version="1.0" enco ...
- 360大牛 全面解读 PHP面试
360大牛全面解读PHP面试 第1章 课程介绍 让大家了解基本面试流程和面试的核心要求以及意义是什么并理解PHP面试考点主要以基础为核心,说明PHP面试考察范围. 第2章 PHP基础知识考察点 本章主 ...
- Day 8 面试题
一.选择题(每题2分) #####1.1 设超级用户root当前所在目录为:/usr/local,键入cd命令后, 用户当前所在目录为(B) A:/home B:/root C:/home/root ...
- [Linux] Telnet提示:Unable to connect to remote host: No route to host
出错现象: [root@localhost ~]# telnet 192.168.1.128 Trying 192.168.1.128...telnet: connect to address 192 ...