上一句话说明了微信批准登录的实现。本文主要介绍QQ授权登录的实现。
其登录过程与微信授权登录大体类似,不过不需要考虑普通H5和公众号两种情况,因此相对来说逻辑处理上要简单些。1. 登录QQ开放平台申请网页应用
待应用审核通过后进入应用修改“平台信息”:
主要是网站回调域的配置,即QQ授权登录成功后返回的页面地址,可以配置多个,通过分号间隔。像上面我针对电脑端和H5页面分别配置了两个地址。
2. 处理流程
本人项目的主要流程如下(具体流程上可能会有不同,根据实际业务场景定):
- 前端嵌入授权登录按钮,点击后跳转到后端组装授权请求地址及参数;
- 前端跳转到后端返回的地址,此时如果是移动端会调起QQ应用进行授权登录,电脑端将会打开二维码;
- 用户授权,成功后QQ将本次授权的Token返回到我们指定的页面中;
- 在指定页面中解析Token,然后在后台通过Token调用QQ接口获取用户OpenId;
- 在应用用户表中根据OpenId查找用户信息,如果已经存在,则直接调起本地登录然后将token返回前端,处理结束;
- 如果未找到用户信息,那么需要调用QQ接口获取用户信息(如昵称等),然后返回前端进行用户账号绑定,绑定完后进行登录生成应用Token返回前端。
3. 实现细节
注意后端使用Vue实现,后端使用Spring Boot。
3.1 前端嵌入登录按钮
<div>
<img
src="~@/asse;
@click="$goPath('/user/login/qq-login')"
/>
</div>
点击后跳转到qq-login页面,然后在qq-login页面中直接调用后端接口获取授权参数,获取成功后即跳转页面:
this.$get("/base/login/qq-login-page", { isMobile: true }).then(
(resp) => {
window.open(resp, "_self");
}
);
3.2 后端授权参数组装接口
先要增加QQ操作的一个公共包,Maven配置:
<dependency>
<groupId>net.gplatform</groupId>
<artifactId>Sdk4J</artifactId>
<version>2.0</version>
</dependency>
然后在项目资源文件目录下增加qqconnec文件,配置信息如下:
app_ID = 10***173
app_KEY = 6bb588****efc4da
redirect_URI =
mobile_redirect_URI =
scope = get_user_info
baseURL =
getUserInfoURL = user/get_user_info
accessTokenURL = oauth2.0/token
authorizeURL = oauth2.0/authorize
getOpenIDURL = oauth2.0/me
addTopicURL = shuoshuo/add_topic
addBlogURL = blog/add_one_blog
addAlbumURL = photo/add_album
uploadPicURL = photo/upload_pic
listAlbumURL = photo/list_album
addShareURL = share/add_share
checkPageFansURL = user/check_page_fans
addTURL = t/add_t
addPicTURL = t/add_pic_t
delTURL = t/del_t
getWeiboUserInfoURL = user/get_info
getWeiboOtherUserInfoURL = user/get_other_info
getFansListURL = relation/get_fanslist
getIdolsListURL = relation/get_idollist
addIdolURL = relation/add_idol
delIdolURL = relation/del_idol
getTenpayAddrURL = cft_info/get_tenpay_addr
getRepostListURL = t/get_repost_list
version = 2.0.0.0
注意配置appId及appKey、redirect_uri,移动端还要配置mobile_redirect_uri。
注意配置的两个redirect_uri必须在QQ开放平台后台配置,也就是本文最开头所述,否则授权登录时将会报异常。
组装授权参数方法实现如下:
/**
* 获取登录地址
*
* @param mobile 是否移动端登录,移动端返回地址与WEB不一样
* @return 登录地址
*/
public String getLoginPageUrl(boolean mobile) {
return QQConnec("authorizeURL").trim()
+ "?client_id=" + QQConnec("app_ID")
+ "&redirect_uri=" + (mobile ? QQConnec("mobile_redirect_URI") : QQConnec("redirect_URI"))
+ "&response_type=token";
}
3.3 前端接收授权成功token
经过以上两步,前端页面将会调起QQ登录;用户同意后QQ将跳转到我们指定的redirect_uri上,并附带token参数;该页面实现如下:
<template>
<div class="qq-login">
<template v-if="u;>
<!-- 绑定用户 -->
<user-bind :initUserInfo="userInfo"></user-bind>
</template>
</div>
</template>
<script>
import userBind from "@/components/UserBind";
export default {
components: { userBind },
props: {},
data() {
return {
token: "",
userInfo: {},
};
},
mounted() {
= (this.$rou);
();
},
methods: {
doLogin() {
this.$get("/base/login/qq", { token: })
.then((resp) => {
if ) {
// 用户已经绑定,登录成功,跳转首页
this.$setCookie("accessToken", re);
this.$cache(
"userInfo",
JSON.stringify)
);
this.$("login", true);
this.$goAfterLogin();
} else {
// 进行用户绑定
= {
qqOpenId: re,
nickname: re,
image: re,
sex: re,
};
}
})
.catch(() => {
this.$me("授权登录失败");
});
},
parseToken(fullPath) {
// QQ返回的access_token是在#后的,需要进行特殊解析
if (fullPath) {
// 取最后一个位置的#
let idx = ("#");
if (idx === -1) {
return null;
}
var restStr = (idx + 1);
if (!restStr) {
return null;
}
restStr = restStr
.split("&")
.find((str) => ("access_token="));
if (!restStr) {
return null;
}
return re(13);
}
},
loginSucceeded() {
this.$goAfterLogin();
},
},
};
</script><style lang="scss">
.qq-login {
.bind-form {
width: 400px;
margin: 0 auto;
padding: 30px 40px 10px 10px;
.title {
line-height: 50px;
font-size: 14px;
}
}
}
</style>
获取到token后会调用后端接口获取openId及用户信息
3.4 后端通过token获取用户信息
前端获取token后传给后台,由后台进行下一步处理,具体处理过程如下:
@GetMapping("/qq")
public ThirdLoginResultDTO qqLogin(@RequestParam("token") String token) {
// 先获取openId
String openId = qqService.getOpenId(token);
ThirdLoginResultDTO result = new ThirdLoginResultDTO();
// 根据openId查询用户是否存在,存在则直接登录
Optional<UserDTO> userOptional = u(openId);
if ()) {
// 如果已经存在,直接登录后返回
UserDTO user = u();
BiValue<String, UserDTO> biValue = loginService.localLogin(user);
re(biValue);
re(true);
return result;
}
// 不存在,返回给前端相关信息并进行绑定
// 如果openId不存在,则需要将昵称等返回前端,在前端关联手机号进行绑定
ThirdUserInfo qqUserInfo = qqService.getUserInfo(token, openId);
re(false);
re(qqUserInfo);
return result;
}
其中QQService 实现如下:
package com.;
import com.liuqi.common.web.common.error.BusinessException;
import com.qq.connect.QQConnectException;
import com.qq.connect.a;
import com.qq.connect.a;
import com.qq.connect.javabeans.qzone.UserInfoBean;
import com.qq.connect.u;
import com.;
import org.;
import org.Factory;
import org.;
import javax.anno;
import javax.;
/**
* QQ登录相关操作服务
*
* @author LiuQi 2020/8/29-15:07
* @version V1.0
**/
@Service
public class QQService {
private static final Logger logger = LoggerFac);
@Resource
private HttpServletRequest request;
/**
* 获取登录地址
*
* @param mobile 是否移动端登录,移动端返回地址与WEB不一样
* @return 登录地址
*/
public String getLoginPageUrl(boolean mobile) {
return QQConnec("authorizeURL").trim()
+ "?client_id=" + QQConnec("app_ID")
+ "&redirect_uri=" + (mobile ? QQConnec("mobile_redirect_URI") : QQConnec("redirect_URI"))
+ "&response_type=token";
}
/**
* 根据Token获取OpenId
*
* @param token 登录Token
* @return OpenId
*/
public String getOpenId(String token) {
try {
return new OpenID(token).getUserOpenID();
} catch (QQConnectException e) {
logger.error("QQ服务调用失败", e);
throw Bu("QQ服务调用失败");
}
}
/**
* 获取QQ用户信息
*
* @param token token
* @return QQ用户信息
*/
public ThirdUserInfo getUserInfo(String token, String openId) {
UserInfo userInfo = new UserInfo(token, openId);
UserInfoBean userInfoBean = null;
try {
userInfoBean = u();
} catch (QQConnectException e) {
logger.error("QQ服务调用失败", e);
throw Bu("QQ服务调用失败");
}
ThirdUserInfo qqUserInfo = new ThirdUserInfo();
qqU().getAvatarURL100());
qqU());
qqU().equals("男") ? 1 : 2);
qqU(openId);
return qqUserInfo;
}
/**
* 获取QQ用户信息
*
* @param token token
* @return QQ用户信息
*/
public ThirdUserInfo getUserInfo(String token) {
String openId = (token);
return (token, openId);
}
}
这样整个QQ授权登录过程就处理完了。相对来说比微信授权登录的要简单很多。)