后端控制vue表单(QQ授权登录前后端实现)

上一篇文章中介绍了微信授权登录的实现,本文将主要讲述qq授权登录的实现。其登录过程与微信授权登录大体类似,不过不需要考虑普通H5和公众号两种情况,因此相对来说逻辑处理上要简单些。

1. 登录QQ开放平台申请网页应用

待应用审核通过后进入应用修改“平台信息”:

后端控制vue表单(QQ授权登录前后端实现)(1)

主要是网站回调域的配置,即QQ授权登录成功后返回的页面地址,可以配置多个,通过分号间隔。像上面我针对电脑端和H5页面分别配置了两个地址。

2. 处理流程

本人项目的主要流程如下(具体流程上可能会有不同,根据实际业务场景定):

后端控制vue表单(QQ授权登录前后端实现)(2)

  • 前端嵌入授权登录按钮,点击后跳转到后端组装授权请求地址及参数;
  • 前端跳转到后端返回的地址,此时如果是移动端会调起QQ应用进行授权登录,电脑端将会打开二维码;
  • 用户授权,成功后QQ将本次授权的Token返回到我们指定的页面中;
  • 在指定页面中解析Token,然后在后台通过Token调用QQ接口获取用户openId;
  • 在应用用户表中根据OpenId查找用户信息,如果已经存在,则直接调起本地登录然后将token返回前端,处理结束;
  • 如果未找到用户信息,那么需要调用QQ接口获取用户信息(如昵称等),然后返回前端进行用户账号绑定,绑定完后进行登录生成应用Token返回前端。
3. 实现细节

注意后端使用Vue实现,后端使用Spring Boot。

3.1 前端嵌入登录按钮

<div> <img src="~@/assets/qq.png" @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>

然后在项目资源文件目录下增加qqconnectconfig.properties文件,配置信息如下:

app_ID = 10***173 app_KEY = 6bb588****efc4da redirect_URI = http://www.ttcn.vip/user/login/qq-login mobile_redirect_URI = http://m.ttcn.vip/user/login/qq-login scope = get_user_info baseURL = https://graph.qq.com/ getUserInfoURL = https://graph.qq.com/user/get_user_info accessTokenURL = https://graph.qq.com/oauth2.0/token authorizeURL = https://graph.qq.com/oauth2.0/authorize getOpenIDURL = https://graph.qq.com/oauth2.0/me addTopicURL = https://graph.qq.com/shuoshuo/add_topic addBlogURL = https://graph.qq.com/blog/add_one_blog addAlbumURL = https://graph.qq.com/photo/add_album uploadPicURL = https://graph.qq.com/photo/upload_pic listAlbumURL = https://graph.qq.com/photo/list_album addShareURL = https://graph.qq.com/share/add_share checkPageFansURL = https://graph.qq.com/user/check_page_fans addTURL = https://graph.qq.com/t/add_t addPicTURL = https://graph.qq.com/t/add_pic_t delTURL = https://graph.qq.com/t/del_t getWeiboUserInfoURL = https://graph.qq.com/user/get_info getWeiboOtherUserInfoURL = https://graph.qq.com/user/get_other_info getFansListURL = https://graph.qq.com/relation/get_fanslist getIdolsListURL = https://graph.qq.com/relation/get_idollist addIdolURL = https://graph.qq.com/relation/add_idol delIdolURL = https://graph.qq.com/relation/del_idol getTenpayAddrURL = https://graph.qq.com/cft_info/get_tenpay_addr getRepostListURL = https://graph.qq.com/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 QQConnectConfig.getValue("authorizeURL").trim() "?client_id=" QQConnectConfig.getValue("app_ID") "&redirect_uri=" (mobile ? QQConnectConfig.getValue("mobile_redirect_URI") : QQConnectConfig.getValue("redirect_URI")) "&response_type=token"; }

3.3 前端接收授权成功token

经过以上两步,前端页面将会调起QQ登录;用户同意后QQ将跳转到我们指定的redirect_uri上,并附带token参数;该页面实现如下:

<template> <div class="qq-login"> <template v-if="userInfo.qqOpenId"> <!-- 绑定用户 --> <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.token = this.parseToken(this.$route.fullPath); this.doLogin(); }, methods: { doLogin() { this.$get("/base/login/qq", { token: this.token }) .then((resp) => { if (resp.success) { // 用户已经绑定,登录成功,跳转首页 this.$setCookie("accessToken", resp.loginInfo.key); this.$cache( "userInfo", JSON.stringify(resp.loginInfo.value) ); this.$store.commit("login", true); this.$goAfterLogin(); } else { // 进行用户绑定 this.userInfo = { qqOpenId: resp.userInfo.openId, nickname: resp.userInfo.nickname, image: resp.userInfo.headImage, sex: resp.userInfo.sex, }; } }) .catch(() => { this.$message.error("授权登录失败"); }); }, parseToken(fullPath) { // QQ返回的access_token是在#后的,需要进行特殊解析 if (fullPath) { // 取最后一个位置的# let idx = fullPath.lastIndexOf("#"); if (idx === -1) { return null; } var restStr = fullPath.substring(idx 1); if (!restStr) { return null; } restStr = restStr .split("&") .find((str) => str.includes("access_token=")); if (!restStr) { return null; } return restStr.substring(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 = userService.findByQQOpenId(openId); if (userOptional.isPresent()) { // 如果已经存在,直接登录后返回 UserDTO user = userOptional.get(); BiValue<String, UserDTO> biValue = loginService.localLogin(user); result.setLoginInfo(biValue); result.setSuccess(true); return result; } // 不存在,返回给前端相关信息并进行绑定 // 如果openId不存在,则需要将昵称等返回前端,在前端关联手机号进行绑定 ThirdUserInfo qqUserInfo = qqService.getUserInfo(token, openId); result.setSuccess(false); result.setUserInfo(qqUserInfo); return result; }

其中QQService 实现如下:

package com.ttcn.front.service; import com.liuqi.common.web.common.error.BusinessException; import com.qq.connect.QQConnectException; import com.qq.connect.api.OpenID; import com.qq.connect.api.qzone.UserInfo; import com.qq.connect.javabeans.qzone.UserInfoBean; import com.qq.connect.utils.QQConnectConfig; import com.ttcn.front.bean.ThirdUserInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * QQ登录相关操作服务 * * @author LiuQi 2020/8/29-15:07 * @version V1.0 **/ @Service public class QQService { private static final Logger logger = LoggerFactory.getLogger(QQService.class); @Resource private HttpServletRequest request; /** * 获取登录地址 * * @param mobile 是否移动端登录,移动端返回地址与WEB不一样 * @return 登录地址 */ public String getLoginPageUrl(boolean mobile) { return QQConnectConfig.getValue("authorizeURL").trim() "?client_id=" QQConnectConfig.getValue("app_ID") "&redirect_uri=" (mobile ? QQConnectConfig.getValue("mobile_redirect_URI") : QQConnectConfig.getValue("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 BusinessException.create("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 = userInfo.getUserInfo(); } catch (QQConnectException e) { logger.error("QQ服务调用失败", e); throw BusinessException.create("QQ服务调用失败"); } ThirdUserInfo qqUserInfo = new ThirdUserInfo(); qqUserInfo.setHeadImage(userInfoBean.getAvatar().getAvatarURL100()); qqUserInfo.setNickname(userInfoBean.getNickname()); qqUserInfo.setSex(userInfoBean.getGender().equals("男") ? 1 : 2); qqUserInfo.setOpenId(openId); return qqUserInfo; } /** * 获取QQ用户信息 * * @param token token * @return QQ用户信息 */ public ThirdUserInfo getUserInfo(String token) { String openId = this.getOpenId(token); return this.getUserInfo(token, openId); } }

这样整个QQ授权登录过程就处理完了。相对来说比微信授权登录的要简单很多。)

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页