preface

A while ago, I added the function of third-party login for my open source project, the implementation process is still smooth, this article will share with you my implementation ideas and process, welcome you interested developers to read this article.

Environment set up

The backend of my project is built based on SpringBoot, so JUSTauth library is directly used here for third-party login.

Introduction of depend on

Add subordinate code to pom.xml.

<! -- Third-party login library -->
<dependency>
    <groupId>me.zhyd.oauth</groupId>
    <artifactId>JustAuth</artifactId>
    <version>1.15.9</version>
</dependency>
<! HTTP request library -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.8.0</version>
</dependency>
Copy the code

The HTTP request library is mostly introduced in the above code, because the new JustAuth removes the HTTP request library by default. You need to introduce the HTTP request library by yourself, otherwise an error will be reported.

Encapsulated utility class

According to the documentation, we encapsulate several utility classes that we need for the platform, as shown below:

package com.lk.utils;

import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.request.*;

import java.util.ArrayList;
import java.util.List;

// Third party authorization login tool class
public class AuthUtil {
    public static AuthRequest getGithubRequest(a) {
        return new AuthGithubRequest(AuthConfig.builder()
                .clientId("Platform id")
                .clientSecret("Platform Key")
                .redirectUri("Callback address")
                .build());
    }

    public static AuthRequest getBaiduRequest(a) {
        return new AuthBaiduRequest(AuthConfig.builder()
                .clientId("")
                .clientSecret("")
                .redirectUri("")
                .build());
    }

    public static AuthRequest getGiteeRequest(a) {
        return new AuthGiteeRequest(AuthConfig.builder()
                .clientId("")
                .clientSecret("")
                .redirectUri("")
                .build());
    }

    // Open Source China license
    public static AuthRequest getOschinaRequest(a) {
        return new AuthOschinaRequest(AuthConfig.builder()
                .clientId("")
                .clientSecret("")
                .redirectUri("")
                .build());
    }

    // Tencent Cloud authorization
    public static AuthRequest getCodingRequest(a) {
        List<String> scopeList = new ArrayList<>();
        scopeList.add("user");
        return new AuthCodingRequest(AuthConfig.builder()
                .clientId("")
                .clientSecret("")
                .redirectUri("")
                .scopes(scopeList)
                .codingGroupName("") .build()); }}Copy the code

Change clientId and clientSecret in the above code into your own account platform, please go to the official document: oauth/github. HTML)

⚠️ Note: when logging in for coding authorization, you need to configure scopes and only one user can be configured, which can only get the basic information of the user. At the beginning, I added an extra email, but the authorization message said THAT I did not have the right to call.

The implementation process

Let’s look at the implementation process.

The backend implementation

Here we need to write two request interfaces for the client to call.

  • Generating authorization links
  • Obtaining User information

Generating authorization links

We need the client to pass a platform name parameter, and then we call the method in the utility class we just wrapped based on the platform name, as follows:

    @apiIMPLICITParams ({@apiIMPLICITParam (name = "platform", value = "platform name ", dataType = "String", paramType = "query", example = "GitHub", required = true) })
    @apiOperation (value = "Obtain third-party login authorization address ", notes =" authorization URL address ")
    // Allow cross-domain access
    @CrossOrigin()
    @RequestMapping(value = "/getAuthorize", method = RequestMethod.GET)
    publicResultVO<? > getAuthorize(@RequestParam(value = "platform") String platform) {
        AuthRequest authRequest = null;
        switch (platform) {
            case "github":
                authRequest = AuthUtil.getGithubRequest();
                break;
            case "gitee":
                authRequest = AuthUtil.getGiteeRequest();
                break;
            case "baidu":
                authRequest = AuthUtil.getBaiduRequest();
                break;
            case "oschina":
                authRequest = AuthUtil.getOschinaRequest();
                break;
            case "coding":
                authRequest = AuthUtil.getCodingRequest();
                break;
            default:
                log.error("Unidentified platform" + platform);
                return ResultVOUtil.error(-1."Platform not identified, no processing found.");
        }
        // Generate a status code
        String state = AuthStateUtils.createState();
        // Generate an authorization link
        String authorizeUrl = authRequest.authorize(state);
        HashMap<String, String> result = new HashMap<>();
        result.put("authorizeUrl", authorizeUrl);
        // Send the status code to the client, and send the state back to the server to ensure the integrity of the request and prevent CSRF risk when obtaining user information after successful authorization
        result.put("state", state);
        return ResultVOUtil.success(result);
    }

Copy the code

Obtaining User information

After the client receives the authorization link, the user agrees to authorize, and the third-party website returns the code. The client carries the State code and code returned by the interface of the authorization link to obtain user information.

The implementation code is as follows:

    @apiOperation (value = "third party login ", notes =" After user authorization, obtain user information from the field returned by the third party website and then log in ")
    // Allow cross-domain access
    @CrossOrigin()
    @RequestMapping(value = "/authorizeLogin", method = RequestMethod.POST)
    publicResultVO<? > authorizeLogin(@apiParam (name = "information returned after passing authorization successfully ", required = true) @Valid @RequestBody GitHubLoginDto loginDto) throws Exception {
        String state = loginDto.getState();
        String code = loginDto.getCode();
        String platform = loginDto.getPlatform();
        AuthRequest authRequest;
        // User informationAuthResponse<? > result; AuthCallback callback =new AuthCallback();
        callback.setState(state);
        callback.setCode(code);
        switch (platform) {
            case "github":
                authRequest = AuthUtil.getGithubRequest();
                result = authRequest.login(callback);
                break;
            case "gitee":
                authRequest = AuthUtil.getGiteeRequest();
                result = authRequest.login(callback);
                break;
            case "baidu":
                authRequest = AuthUtil.getBaiduRequest();
                result = authRequest.login(callback);
                break;
            case "oschina":
                authRequest = AuthUtil.getOschinaRequest();
                result = authRequest.login(callback);
                break;
            case "coding":
                authRequest = AuthUtil.getCodingRequest();
                result = authRequest.login(callback);
                break;
            default:
                log.error("Unidentified platform" + platform);
                return ResultVOUtil.error(-1."Platform not identified, user information failed to obtain");
        }
        if (result.getData() == null) {
            // Authorization failed, and an error message is returned
            return ResultVOUtil.error(-1."Authorization failed :" + result.getMsg());
        }
        JSONObject data = new JSONObject(result.getData());
        log.info(platform + "User information:" + data);
        
        return LoginUtil.getLoginResult(data);
    }

Copy the code

The front-end implementation

Let’s look at the implementation of the front end.

Creating an Authorization Page

The backend implementation section, obtain authorization link, configured with a callback address, this address will be automatically stitching on the third party platform return code code, so we need to create an HTML page, used to get the address bar code in the code, will get the code into local storage, convenient we access to the login page.

The implementation code is as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Authorization callback page</title>
  <script type="text/javascript">
    /** * get the URL argument *@param url
     * @param variable* /
    function getQueryVariable(url, variable) {
      // Intercepts the URL
      url = url.substring(url.indexOf("?"), url.length);
      const query = url.substring(1);
      const vars = query.split("&");
      for (let i = 0; i < vars.length; i++) {
        const pair = vars[i].split("=");
        if (pair[0] === variable) {
          return pair[1]; }}return -1;
    }
    window.onload = () = > {
      // Get the authorization code in the URL
      const code = getQueryVariable(window.location.href, "code");
      // Put the authorization code into local storage
      if(code ! = = -1) {
        localStorage.setItem("authCode", code);
      }else {
        localStorage.setItem("authCode"."");
      }
      // Close the page
      window.close();
    }
  </script>
</head>
<body>

</body>
</html>
Copy the code

Open the Authorization page

Next, we need to open the authorization page returned by the back-end interface on the login page. After the user is successfully authorized, the code and state are sent back to the server to realize the login.

Then, what we need to consider next is how to get the code returned by the third-party platform to call the authorization login interface immediately after the user succeeds in authorization after opening the authorization page in the form of popup.

After some reflection, I came up with the following ideas:

  • Use window.open() to open the authorization window
  • Listen for localStorage changes on the login page
  • The authorization page writes the code to localStorage
  • The login page detects changes and determines whether the user is successfully authorized according to the code
  • If the authorization succeeds, you can log in

The implementation code is as follows:

<! -- Login page -->
<template>
    <! -- Third-party login -->
    <div
      class="auth-panel"
      v-if="isLoginStatus === loginStatusEnum.NOT_LOGGED_IN"
    >
      <div class="item-panel" @click.once="getAuthorize('github')">
        <img src="@/assets/img/auth/github.png" alt="Making login" />
      </div>
      <div class="item-panel" @click.once="getAuthorize('gitee')">
        <img src="@/assets/img/auth/gitee.png" alt="Gitee login" />
      </div>
      <div class="item-panel" @click.once="getAuthorize('baidu')">
        <img src="@/assets/img/auth/baidu.png" alt="Baidu Login" />
      </div>
      <div class="item-panel" @click.once="getAuthorize('oschina')">
        <img src="@/assets/img/auth/oschina.png" alt="Open Source China login" />
      </div>
      <div class="item-panel" @click.once="getAuthorize('coding')">
        <img src="@/assets/img/auth/coding.png" alt=Tencent Cloud Login />
      </div>
    </div>
</template>

<script lang="ts">
  name: "login".data(){
      state: "".platform: ""  
  },  
  methods: {
    getAuthorize: function(name: string) {
      // Get the authorization link
      this.$api.authLoginAPI
        .getAuthorize({ platform: name })
        .then((res: responseDataType<getAuthorizeDataType>) = > {
          if(! res.data.state || ! res.data.authorizeUrl)throw "Server error: obtaining authorization link failed";
          const authorizeUrlres = res.data.authorizeUrl;
          // Update the status code and login platform name
          this.state = res.data.state;
          this.platform = name;
          // Open the authorization window
          window.open(
            authorizeUrlres,
            "_blank"."toolbar=no,width=800, height=600"
          );
          // Start listening to localStorage to obtain the authorization code
          window.addEventListener("storage".this.getAuthCode);
        });
    },
    getAuthCode: function() {
      // Obtain the authorization code
      const code = localStorage.getItem("authCode");
      localStorage.removeItem("authCode");
      // Remove localStorage listener
      this.removeStorageListener();
      if (code) {
        // Call the login function
        this.authLogin(this.state, code, this.platform);
        return;
      }
      throw this.platform + "Obtaining authorization code failed";
    },
    authLogin: function(state: string, code: string, platform: string) {
      this.$api.authLoginAPI
        .authorizeLogin({
          state: state,
          code: code,
          platform: platform
        })
        .then((res: responseDataType) = > {
          if (res.code == 0) {
            // Store the current user information
            
            return;
          }
          // Switch back to the login page}); }}</script>
Copy the code

Implementation effect

Now let’s take a look at the final result.

The project address

  • Online experience address: chat-system
  • GitHub address: chat-system-github

Write in the last

  • If there are any errors in this article, please correct them in the comments section. If this article helped you, please like it and follow 😊
  • This article was first published in nuggets. Reprint is prohibited without permission 💌