Vue integrated QQ login [server-side mode] [Oauth2 code mode]

Apply for QQ Internet Login

  1. Document address: [QQ login] OAuth2.0 development document.
  2. Apply for an application address
    Precautions:
    1. Add a QQ login button on the homepage of the website (how to add it will be explained below)
    2. The website information is consistent with the record information

model

This article uses the server-side mode
Also known as Web Server Flow;
Suitable for applications that need to be accessed from a web server, such as Web/wap websites.

The schematic diagram of the authorization verification process is as follows (image source: Section 4.1 of OAuth2.0 Protocol Draft V21 )

For the application, two steps are required:

  1. Get Authorization Code;
  2. Obtain Access Token through Authorization Code

Introduce resources

Introduce js in public.index.html

  <script type="text/javascript" src="http://qzonestyle.gtimg.cn/qzone/openapi/qc_loader.js" data-callback="true"
    data-appid="APPID" data-redirecturi="callback address" charset="utf-8">
    </script>

The function of the parameter data-callback="true" is to automatically close the callback page for you after the authorization is completed. Ask what you need to close the page. The following will say

Add buttons and functions

  1. add button
              <span>Server-side model</span>
              <img
                src="../../assets/image/bt_blue_76X24.png"
                @click="doQQLogin()"
              >
    
  2. add function
    Click the button to jump to the authorization page of QQ Internet with relevant parameters. After the authorization is completed, you will get the authorization code and call back to the callback page.
	 doQQLogin() {
      //Get QQ connection

      getqqLoginInfo().then((res) => {
        window.location.href =
          "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" +
          res.data.qqAppId +
          "&redirect_uri=" +
          res.data.qqRedirectUri +
          "&state=" +
          res.data.state;
      });
    },

callback page

This page is an interface called by Tencent after the page QQ login is completed. The parameter ?code=XXXXXXXXXXXXXXXXXXX XX will be spliced ​​after the interface. The function of this page is to send the code to the background after receiving the code, and after the background receives it, I will go to QQ Internet to exchange for tokens. Note that this code is one-time and burns up after use. After obtaining the token, you can obtain user information according to the token and AppId, so that you can perform the login, and jump to the home page after the login is completed.

<template>

</template>

<script>
import { qqCallback } from "@/api/index";

export default {
  name: "qqCallback",

 
  mounted() {
    var accessToken = this.$utils.getUrlKey("access_token");
    var code = this.$utils.getUrlKey("code");
    //client_side mode
    if (accessToken && typeof accessToken != "undefined") {
      console.log("client_side model:" + accessToken);
      if (QC.Login.check()) {
        this.$message.info("qq successfully logged in");
        this.$store.dispatch("user/qqlogin", accessToken).then(() => {
          this.$router.push("/");
        });
      }
    }

    //server-side mode
    if (code && typeof code != "undefined") {
      console.log("server-side model:" + code);
      this.$store.dispatch("user/qqCodelogin", code).then(() => {
        this.$router.push("/");
      });
    }
  },
};
</script>

<style>
</style>

backend interface

rest interface

     /**
     * desc: When the front end obtains the authorization code of the QQ interconnection callback, it transmits the authorization code to the background, obtains the access_token, and verifies the token to perform the login, and returns the token of the current system.
     *
     * @return cn.com.zsw.gblog.vo.R
     * @author: shiwangZhou
     * @date: 2020-09-23 8:40
     * @param: code  QQ Authorization code for interconnect callback
     */
    @GetMapping("qq/token")
    public R getAccessTokenByCode(String code) {
        String accessTokenByCode = oauthClient.getAccessTokenByCode(qqAppId, appKey, code, qqRedirectUri);
        log.info("ask access_token return result:" + accessTokenByCode);
        GetAccessTokenDTO accessTokenDTO = UrlUtils.paramToDTO(accessTokenByCode, GetAccessTokenDTO.class);
        String token = callback(accessTokenDTO.getAccess_token());
        Assert.notBlank(token, "failed to get token,Login failed");
        return R.SUCCESS.data(token);

    }
   private String callback(String accessToken) {
        String openIdStr = oauthClient.getOpenId(accessToken);
        log.info("ask openID return result:" + openIdStr);
        String substring = openIdStr.substring(10, openIdStr.length() - 4);
        GetOpenIdRes openIdRes = JSON.parseObject(substring, GetOpenIdRes.class);
        GetUsrInfoRes userInfo = oauthClient.getUserInfo(accessToken, openIdRes.getClient_id(), openIdRes.getOpen_id());
        ThirdPartUser thirdPartUser = thirdPartUserService.getOne(Wrappers.<ThirdPartUser>lambdaQuery().eq(ThirdPartUser::getOpenId,
                openIdRes.getOpen_id()));
        GbUser qwe = userService.getOne(Wrappers.<GbUser>lambdaQuery().eq(GbUser::getUsername, "qwe"));
        UserDetails userDetails = userDetailsService.loadUserByUsername(qwe.getUsername());

        if (thirdPartUser == null) {
            thirdPartUser = new ThirdPartUser();
            thirdPartUser.setAvatar(userInfo.getAvatar());
            thirdPartUser.setGender("male".equalsIgnoreCase(userInfo.getGender()) ? 1 : 0);
            thirdPartUser.setId(IdUtil.createSnowflake(1, 1).nextId());
            thirdPartUser.setNickname(userInfo.getNickname());
            thirdPartUser.setUserId(qwe.getId());
            thirdPartUser.setOpenId(openIdRes.getOpen_id());
            thirdPartUser.setUserInfo(JSON.toJSONString(userInfo));
            thirdPartUserService.save(thirdPartUser);
        } else {
            userDetails = userDetailsService.loadUserByUsername(qwe.getUsername());
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        String id = IdUtil.createSnowflake(1, 1).nextIdStr();
        redisCache.setCacheObject(SysConstants.REDIS_JWT_TOKEN_ID + id, JSON.toJSONString(userDetails), 2, TimeUnit.HOURS);
        return id;
    }

Summarize:

  1. The code mode is called by the backend, which is safer than the simplified mode
  2. Simplified mode makes it easier to connect

Code address:

front-end gitee
backend gitee

Tags: Java Spring Boot Vue oauth

Posted by rrijnders on Sun, 15 May 2022 10:42:06 +0300