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 ...
随机推荐
- codeforces 14E. Camels(多维dp)
题目链接:http://codeforces.com/problemset/problem/14/E 题意:就是给出n个点要求画出t个波峰和t-1个波谷 很显然要t个波峰和t-1个波谷开始是波动一定是 ...
- 最长上升子序列模板 hdu 1087 Super Jumping! Jumping! Jumping!
Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. May ...
- 章节十六、6-xml参数化and并行case
一.读取xml文件中参数 1.案例演示--->创建一个需要读取数据的类 package testclasses; import org.testng.annotations.Test; impo ...
- Https、OpenSSL自建CA证书及签发证书、nginx单向认证、双向认证及使用Java访问
0.环境 本文的相关源码位于 https://github.com/dreamingodd/CA-generation-demo 必须安装nginx,必须安装openssl,(用apt-get upd ...
- 正确应用Java数组
一.数组的特点 数组与其他容器的区别有三方面:效率.类型和保存基本类型的能力. 1.效率.数组是一种效率最高的存储和随机访问对象引用序列的方式.数组是一段连续地址空间内的线性序列,所以访问非常快.但也 ...
- android中的后退键——onBackPressed()的使用
转自:http://blog.sina.com.cn/s/blog_5085156c0101725e.html 很多网友不明白如何在Android平台上捕获Back键的事件,Back键是手机上的后退键 ...
- HBase 官方文档0.90.4
HBase 官方文档0.90.4 Copyright © 2010 Apache Software Foundation, 盛大游戏-数据仓库团队-颜开(译) Revision History Rev ...
- centos 7.x安装 jdk1.8
参考链接即可:本人已经操作实现,才推荐出来 https://blog.csdn.net/weixin_42266606/article/details/80863781
- Ubuntu18.04安装测试TensorFlow-GPU
1 安装Ubuntu18.04.03 lts spt@spt-ts:~$ lsb_release -a No LSB modules are available. Distributor ID: Ub ...
- TLC5615
#include <reg51.h> #include "TLC5615.c" code uchar seven_seg[] = {0xc0, 0xf9, 0xa4, ...