准备材料

创建应用

  • 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)的更多相关文章

  1. .NET Core+QQ第三方授权登录

    安装包 dotnet add package AspNet.Security.OAuth.QQ 接上文GitHub第三方授权登录 申请过程不介绍了,申请者资料,个人也是可以申请成功的. 这时候有二个参 ...

  2. C# winform C/S WebBrowser qq第三方授权登录

    qq的授权登录,跟微信相似,不同的地方是: 1 申请appid与appkey的时候,注意填写回调地址. 2 这里可以在WebBrowser的是Navigated事件中直接得到Access Token, ...

  3. 【第二十一篇】手C# MVC 微信授权登录 OAuth2.0授权登录

    首先一定要熟读,最起码过一遍微信开发者文档 微信开发者文档 文档写的很清楚 授权登录四步走 在正文开始前,我得讲清楚一个事情 敲黑板,划重点:微信一共有两个 access_token 一个是7200就 ...

  4. 什么是“QQ登录OAuth2.0”

    1. 什么是“QQ登录OAuth2.0 OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他 ...

  5. 【转】【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料

    转载请注明出处:http://blog.csdn.net/yangyu20121224/article/details/9057257 由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字 ...

  6. 【Android应用开发详解】实现第三方授权登录、分享以及获取用户资料

      由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折,查阅了一番资料,做了一个Demo.实现起来的效果还是不错的,不敢独享,决定写一个总结的教程,供大家互相 ...

  7. 使用ShareSDK实现第三方授权登录、分享以及获取用户资料效果,项目中包含:源码+效果图+项目结构图

    [Android应用开发详解]第01期:第三方授权认证(一)实现第三方授权登录.分享以及获取用户资料   由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折, ...

  8. SpringBoot基于JustAuth实现第三方授权登录

    1. 简介   随着科技时代日渐繁荣,越来越多的应用融入我们的生活.不同的应用系统不同的用户密码,造成了极差的用户体验.要是能使用常见的应用账号实现全应用的认证登录,将会更加促进应用产品的推广,为生活 ...

  9. github 授权登录教程与如何设计第三方授权登录的用户表

    需求:在网站上想评论一篇文章,而评论文章是要用户注册与登录的,那么怎么免去这麻烦的步骤呢?答案是通过第三方授权登录.本文讲解的就是 github 授权登录的教程. 效果体验地址:http://biao ...

随机推荐

  1. BALNUM - Balanced Numbers(数位dp)

    题目链接:http://www.spoj.com/problems/BALNUM/en/ 题意:问你在[A,B]的闭区间内有几个满足要求的数,要求为每个出现的奇数个数为偶数个,每个出现的偶数个数为奇数 ...

  2. 1 PY环境与变量

    一 环境与文件形式 1.环境搭建http://jingyan.baidu.com/article/eae07827f7f2d01fec5485f7.html 2. python 则进入交互模式  ex ...

  3. Python作业本——第3章 函数

    今天看完了第三章,习题都是一些概念性的问题,就不一一解答了. 实践项目是创建一个Collatz序列,并且加上验证输入是不是一个整数. def collatz(number): if number % ...

  4. JDK、Spring和Mybatis中使用到的设计模式

    一.JDK中的设计模式 (1)结构性模式 1.适配器模式 java.util.Arrays#asList() java.io.InputStreamReader(InputStream) java.i ...

  5. Spark、BulkLoad Hbase、单列、多列

    背景 之前的博客:Spark:DataFrame写HFile (Hbase)一个列族.一个列扩展一个列族.多个列 用spark 1.6.0 和 hbase 1.2.0 版本实现过spark BulkL ...

  6. HDFS原理及操作

    1 环境说明 部署节点操作系统为CentOS,防火墙和SElinux禁用,创建了一个shiyanlou用户并在系统根目录下创建/app目录,用于存放 Hadoop等组件运行包.因为该目录用于安装had ...

  7. OPC-System.Runtime.InteropServices.COMException:“Error HRESULT E_FAIL has been returned from a call to a COM component.”

    Error HRESULT E_FAIL has been returned from a call to a COM component的错误提示,还是因为OPC配置,这次是红框中标签定义错误,应该 ...

  8. Linux 笔记 - 第十九章 配置 Squid 正向代理和反向代理服务

    一.简介 Squid 是一个高性能的代理缓存服务器,对应中文的乌贼,鱿鱼的意思.Squid 支持 FTP,gopher 和 HTTP 协议.和一般的代理缓存软件不同,Squid 用一个单独的,非模块化 ...

  9. [Spark] 04 - HBase

    BHase基本知识 基本概念 自我介绍 HBase是一个分布式的.面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”. ...

  10. BCD 码、Gray 码、ASCII 码都是什么呢?

    BCD 码:即(Binary Coded Decimal)码,也称为 8421 码,是十进制代码中最常见的一种.每一位的 1 代表的十进制数称为这一位的权.BCD 码中每一位的权都是固定不变的,它属于 ...