Vue+Spring Boot的QQ授权登录前后端实现

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

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

QQ授权登录前后端实现(Vue Spring Boot)

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

2. 处理流程

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

QQ授权登录前后端实现(Vue Spring Boot)
  • 前端嵌入授权登录按钮,点击后跳转到后端组装授权请求地址及参数;
  • 前端跳转到后端返回的地址,此时如果是移动端会调起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.gplatformgroupId>
    <artifactId>Sdk4JartifactId>
    <version>2.0version>
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 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授权登录过程就处理完了。相对来说比微信授权登录的要简单很多。)

内容出处:,

声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时修改或删除。文章链接:http://www.yixao.com/procedure/18763.html

发表评论

登录后才能评论