Play QQ login from zero

Posted by MadRhino on Thu, 20 Jan 2022 08:12:09 +0100

QQ login from zero to third-party login

preface

Before we really start docking, let's talk about the scheme design of the background. Since it is connected to the third-party login, it is inevitable how to save the user information. First of all, it should be clear that after the user successfully logs in to the third party, All we can get is an ID representing the user's unique identity (microblog is a real uid, QQ is an encrypted openID) and an accessToken used to identify the user's identity. Of course, there are limited information such as nickname, avatar and gender, The key to third-party login is how to determine that the user is logged in legally. If it is determined that the login and the last login are the same person and are not fake. In fact, we don't have to worry about this. Take microblog login as an example, After successful login, the user will call back a code to us, and then we will take the code to the microblog in exchange for the accessToken. If the code is filled in by the user, it will certainly not pass this level. Therefore, the previous worry is a little superfluous, ha ha.

1. Know oauth2 0

Now many websites need to log in with a third-party account, whether for drainage or user convenience. Today, take QQ login as an example to realize the simplest third-party login. At present, the mainstream third-party login depends on oauth2 0, the most common is QQ login, wechat login and so on in various small and medium-sized websites or apps. So I suggest students who want to learn and implement the third-party login to understand this protocol.

The domain name must be and filed

For example, my domain name: https://yangbuyi.top/ Because Tencent has a domain name authentication mechanism......

2. Real name authentication QQ login, we connect to QQ Internet, address: https://connect.qq.com First, you need to register as a developer and authenticate your real name. You need to hold an ID card photo. I won't talk about the details.

2.1. Apply for developer identity

2.2 creating applications

Enter the application management page to create an application. According to the actual needs, create a website application or a mobile application. Here is a website application:

After the submission is successful, wait for the customer service to review

This is the basic interface information of my website

QQ login process

Request parameters

Create springboot project

rely on

        <!-- qq Login integration started -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpasyncclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
        </dependency>
        <!--json Conversion tool-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <!--QQSDK-->
        <dependency>
            <groupId>net.gplatform</groupId>
            <artifactId>Sdk4J</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <!-- qq End of login integration -->


        <!-- Template -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


        <!-- Other configurations -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

Create http request tool

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * description:  Yang Buyi website: www.yangbuyi.com top
 * ClassName:  HttpsUtils
 * create:  2020-06-24 17:30
 *
 * @author: yangbuyi
 * @since:  JDK1.8
 **/

public class HttpsUtils {
      private static PoolingHttpClientConnectionManager connMgr;
      private static RequestConfig requestConfig;
      private static final int MAX_TIMEOUT = 7000;
      
      private static final Logger logger = LoggerFactory.getLogger(HttpsUtils.class);
      
      static {
            // Set up connection pool
            connMgr = new PoolingHttpClientConnectionManager();
            // Set connection pool size
            connMgr.setMaxTotal(100);
            connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
            // Validate connections after 1 sec of inactivity
            connMgr.setValidateAfterInactivity(1000);
            RequestConfig.Builder configBuilder = RequestConfig.custom();
            // Set connection timeout
            configBuilder.setConnectTimeout(MAX_TIMEOUT);
            // Set read timeout
            configBuilder.setSocketTimeout(MAX_TIMEOUT);
            // Sets the timeout for getting connection instances from the connection pool
            configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
            
            requestConfig = configBuilder.build();
      }
      
      /**
       * Send GET request (HTTP) without input data
       *
       * @param url
       * @return
       */
      public static String doGet(String url) {
            return doGet(url, new HashMap<String, Object>());
      }
      
      /**
       * Send GET request (HTTP) in K-V format
       *
       * @param url
       * @param params
       * @return
       */
      public static String doGet(String url, Map<String, Object> params) {
            String apiUrl = url;
            StringBuffer param = new StringBuffer();
            int i = 0;
            for (String key : params.keySet()) {
                  if (i == 0)
                        param.append("?");
                  else
                        param.append("&");
                  param.append(key).append("=").append(params.get(key));
                  i++;
            }
            apiUrl += param;
            String result = null;
            HttpClient httpClient = null;
            if (apiUrl.startsWith("https")) {
                  httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                        .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
            } else {
                  httpClient = HttpClients.createDefault();
            }
            try {
                  HttpGet httpGet = new HttpGet(apiUrl);
                  HttpResponse response = httpClient.execute(httpGet);
                  HttpEntity entity = response.getEntity();
                  if (entity != null) {
                        InputStream instream = entity.getContent();
                        result = new BufferedReader(new InputStreamReader(instream)).lines().collect(Collectors.joining(System.lineSeparator()));
                  }
            } catch (IOException e) {
                  logger.error(e.getMessage());
            }
            return result;
      }
      
      /**
       * Send POST request (HTTP) without input data
       *
       * @param apiUrl
       * @return
       */
      public static String doPost(String apiUrl) {
            return doPost(apiUrl, new HashMap<String, Object>());
      }
      
      /**
       * Send POST request in K-V format
       *
       * @param apiUrl API Interface URL
       * @param params Parameter map
       * @return
       */
      public static String doPost(String apiUrl, Map<String, Object> params) {
            CloseableHttpClient httpClient = null;
            if (apiUrl.startsWith("https")) {
                  httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                        .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
            } else {
                  httpClient = HttpClients.createDefault();
            }
            String httpStr = null;
            HttpPost httpPost = new HttpPost(apiUrl);
            CloseableHttpResponse response = null;
            
            try {
                  httpPost.setConfig(requestConfig);
                  List<NameValuePair> pairList = new ArrayList<>(params.size());
                  for (Map.Entry<String, Object> entry : params.entrySet()) {
                        NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
                        pairList.add(pair);
                  }
                  httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
                  response = httpClient.execute(httpPost);
                  HttpEntity entity = response.getEntity();
                  httpStr = EntityUtils.toString(entity, "UTF-8");
            } catch (IOException e) {
                  logger.error(e.getMessage());
            } finally {
                  if (response != null) {
                        try {
                              EntityUtils.consume(response.getEntity());
                        } catch (IOException e) {
                              logger.error(e.getMessage());
                        }
                  }
            }
            return httpStr;
      }
      
      /**
       * Send POST request in JSON form
       *
       * @param apiUrl
       * @param json   json object
       * @return
       */
      public static String doPost(String apiUrl, Object json) {
            CloseableHttpClient httpClient = null;
            if (apiUrl.startsWith("https")) {
                  httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                        .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
            } else {
                  httpClient = HttpClients.createDefault();
            }
            String httpStr = null;
            HttpPost httpPost = new HttpPost(apiUrl);
            CloseableHttpResponse response = null;
            
            try {
                  httpPost.setConfig(requestConfig);
                  StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// Solve the problem of Chinese garbled code
                  stringEntity.setContentEncoding("UTF-8");
                  stringEntity.setContentType("application/json");
                  httpPost.setEntity(stringEntity);
                  response = httpClient.execute(httpPost);
                  HttpEntity entity = response.getEntity();
                  httpStr = EntityUtils.toString(entity, "UTF-8");
            } catch (IOException e) {
                  logger.error(e.getMessage());
            } finally {
                  if (response != null) {
                        try {
                              EntityUtils.consume(response.getEntity());
                        } catch (IOException e) {
                              logger.error(e.getMessage());
                        }
                  }
            }
            return httpStr;
      }
      
      /**
       * Create SSL secure connection
       *
       * @return
       */
      private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
            SSLConnectionSocketFactory sslsf = null;
            try {
                  SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                        
                        @Override
                        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                              return true;
                        }
                  }).build();
                  sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
                        
                        @Override
                        public boolean verify(String arg0, SSLSession arg1) {
                              return true;
                        }
                  });
            } catch (GeneralSecurityException e) {
                  logger.error(e.getMessage());
            }
            return sslsf;
      }
      
      /*gitHub start*/
      /**
       * Send the get request and send the request using java code
       * @param url
       * @return
       * @throws Exception
       */
      public static String doGetHub(String url) throws Exception{
            
            CloseableHttpClient httpclient = HttpClients.createDefault();
            
            HttpGet httpGet = new HttpGet(url);
            // An http request was sent
            CloseableHttpResponse response = httpclient.execute(httpGet);
            // If the response 200 is successful, parse the response result
            if(response.getStatusLine().getStatusCode()==200){
                  // Get the content of the response
                  HttpEntity responseEntity = response.getEntity();
                  
                  return EntityUtils.toString(responseEntity);
            }
            return null;
      }
      /**
       * Convert string to map
       * @param responseEntity
       * @return
       */
      public static Map<String,String> getMap(String responseEntity) {
            
            Map<String, String> map = new HashMap<>();
            // Parse string with &
            String[] result = responseEntity.split("\\&");
            
            for (String str : result) {
                  // Parse string with =
                  String[] split = str.split("=");
                  // Store string in map
                  if (split.length == 1) {
                        map.put(split[0], null);
                  } else {
                        map.put(split[0], split[1]);
                  }
                  
            }
            return map;
      }
      
      /**
       * Get map through json
       * @param responseEntity
       * @return
       */
      public static Map<String,String> getMapByJson(String responseEntity) {
            Map<String, String> map = new HashMap<>();
            // Alibaba fastjson converts json into map
            JSONObject jsonObject = JSONObject.parseObject(responseEntity);
            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                  String key = entry.getKey();
                  // Convert obj to string
                  String value = String.valueOf(entry.getValue()) ;
                  map.put(key, value);
            }
            return map;
      }
      /*gitHub end*/
      
      
}

Create a cross domain configuration class in case of cross domain problems

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * ClassName: CorsAutoConfig
 *
 * @author yangshuai
 * @Date: 2021-04-13 14:54
 * @Description: $
 **/
@Configuration
public class CorsAutoConfig {
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        // Indicates what domain name is cross domain * indicates that all domains are cross domain
        corsConfiguration.addAllowedOrigin("*");
        // Inject it
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);

        CorsFilter corsFilter = new CorsFilter(urlBasedCorsConfigurationSource);
        return corsFilter;
    }
}

Create Logincontroller

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.yangbuyi.QQ.OAuthProperties;
import top.yangbuyi.QQ.vo.QQDTO;
import top.yangbuyi.QQ.vo.QQOpenidDTO;
import top.yangbuyi.common.HttpsUtils;

import javax.management.RuntimeErrorException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.server.PathParam;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @description: Yang Buyi website: www.yangbuyi.com top
 * @program: qqlogindemo
 * @ClassName: loginController
 * @create: 2020-08-18 14:41
 * @author: yangbuyi
 * @since:  JDK1.8
 * @loginController: Third party QQ login
 **/

@Controller
@Slf4j
@RequestMapping("api")
public class loginController {

	  /**
	   * Authentication parameters
	   */
	  @Autowired
	  private OAuthProperties oauth;


	  /**
	   * Call QQ login interface
	   * Process: first call the interface to get the code, and then get access according to the code_ Token, which is used to obtain the corresponding user information according to the token
	   * @param response
	   */
	  @GetMapping("/login/oauth")
	  public void loginQQ(  HttpServletResponse response) {
			// Redirect access to QQ login server
			try {
				  response.sendRedirect(oauth.getQQ().getCode_callback_uri() + //Get code address
						  "?client_id=" + oauth.getQQ().getClient_id() //appid
						  +"&state=" + UUID.randomUUID() + //This is said to be anti attack, just give a random uuid
						  "&redirect_uri=" + oauth.getQQ().getRedirect_uri() +//This is very important. This is the callback address, that is, the code code returned by Tencent
						  "&response_type=code");
			} catch (IOException e) {
				  e.printStackTrace();
			}
	  }


	  /**
	   * Callback address set on qq platform
	   *
	   * Receive the code brought from the callback address
	   *
	   * @param code
	   * @param request
	   * @return
	   */
	  @GetMapping("/oauth2")
	  public String authorizeQQ(String code, HttpServletRequest request) {
			HashMap<String, Object> params = new HashMap<>();
			params.put("code", code);
			params.put("grant_type", "authorization_code");
			params.put("redirect_uri", oauth.getQQ().getRedirect_uri());
			params.put("client_id", oauth.getQQ().getClient_id());
			params.put("client_secret", oauth.getQQ().getClient_secret());

			// Get Tencent access token
			Map<String, String> reulsts = getAccess_token(params);
			System.out.println("Traverse the obtained data:");
			for (Map.Entry<String, String> entry : reulsts.entrySet()) {
				  System.out.println(entry.getKey() + "=" + entry.getValue());
			}
			System.out.println("Traversal complete");

			//Access here_ The token has been handled
			//Next, get the openid. Only when you get the openid can you get the user information
			String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + "?access_token=" + reulsts.get("access_token"));
			// callback( {"client_id":"101887062","openid":"74DD1353321FD56375F34422D833848D"} );
			System.out.println("openidContent: " + openidContent);

			//Next, handle openid
			//Intercept the required part of the json string
			String openid = openidContent.substring(openidContent.indexOf("{"), openidContent.indexOf("}") + 1);
			// json to object
			Gson gson = new Gson();
			//Convert the returned openid into DTO
			QQOpenidDTO qqOpenidDTO = gson.fromJson(openid, QQOpenidDTO.class);

			// Encapsulate parameter request user information data
			params.clear();
			//Set access_token
			params.put("access_token", reulsts.get("access_token"));
			//Set openid
			params.put("openid", qqOpenidDTO.getOpenid());
			//Set appid
			params.put("oauth_consumer_key", qqOpenidDTO.getClient_id());
			//Get user information
			String userInfo = HttpsUtils.doGet(oauth.getQQ().getUser_info_callback_uri(), params);
			QQDTO qqDTO = gson.fromJson(userInfo, QQDTO.class);
			// (normally, you can use openid as the user name during development and define your own password.)
			try {

				  /* Assembly data */
				  HashMap<String, Object> map = new HashMap<>();
				  map.put("user", qqDTO);
				  map.put("qqOpenidDTO", qqOpenidDTO);
				  request.setAttribute("map", map);
				  log.info("user data:{}" + qqDTO);
				  log.info("qqOpenidDTO data:{}" + qqOpenidDTO);
				  return "home";
			} catch (Exception e) {
				  e.printStackTrace();
				  return "login";
			}
	  }


	  /**
	   * Get Tencent access_token
	   *
	   * @return
	   */
	  public Map<String, String> getAccess_token(HashMap<String, Object> params) {
			// Authentication address
			//Get access_token such as access_token=9724892714FDF1E3ED5A4C6D074AF9CB&expires_ in=7776000&refresh_ token=9E0DE422742ACCAB629A54B3BFEC61FF
			String result = HttpsUtils.doGet(oauth.getQQ().getAccess_token_callback_uri(), params);
			//Cut the string of the obtained data
			String[] strings = result.split("&");
			//Put it into the map after cutting
			Map<String, String> reulsts = new HashMap<>();
			for (String str : strings) {
				  String[] split = str.split("=");
				  if (split.length > 1) {
						reulsts.put(split[0], split[1]);
				  }
			}
			return reulsts;
	  }


}

Create QQ parameter entity class

Create OAuthProperties to dynamically obtain parameters with yml configuration file

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * description:  Yang Buyi website: www.yangbuyi.com top
 * ClassName:  OAuthProperties
 * create:  2020-06-24 17:06
 *
 * @author: yangbuyi
 * @since:  JDK1.8
 * <p>
 * Get Code
 **/

@Component
//Corresponding to application Parameter under oauth in YML
@ConfigurationProperties(prefix = "oauth")
public class OAuthProperties {
      
      //Get applicaiton All parameters under YML and qq
      private QQProperties qq = new QQProperties();
      
      public QQProperties getQQ() {
            return qq;
      }
      
      public void setQQ(QQProperties qq) {
            this.qq = qq;
      }
}

Create QQProperties parameters for requesting qq

import lombok.Data;
import org.springframework.stereotype.Component;

/**
 * description:  Yang Buyi website: www.yangbuyi.com top
 * ClassName:  QQProperties
 * create:  2020-06-24 17:04
 *
 * @author: yangbuyi
 * @since:  JDK1.8
 *
 * Integrate third-party login QQ parameters
 **/
@Data
@Component
public class QQProperties {
      /**
       * Your appid
       */
      private String client_id;
      /**
       *  #Your appkey
       */
      private String client_secret;
      /**
       * You receive the response code address
       */
      private String redirect_uri;
      /**
       * Tencent get code address
       */
      private String code_callback_uri;
      /**
       * Tencent get access_token address
       */
      private String access_token_callback_uri;
      /**
       * Tencent obtains openid address
       */
      private String openid_callback_uri;
      /**
       * Tencent obtains user information address
       */
      private String user_info_callback_uri;
      
      
      /**
       * Which website do you want to call back to
       */
      private String redirect_url_index_yby;
      private String redirect_url_login_yby;
    
}

Create QQOpenidDTO to obtain access_token,openid

import lombok.Data;

/**
 * description:  Yang Buyi website: www.yangbuyi.com top
 * ClassName:  QQOpenidDTO
 * create:  2020-06-24 17:19
 *
 * @author: yangbuyi
 * @since:  JDK1.8
 *
 * Used to get access_token,openid
 **/
@Data

public class QQOpenidDTO {
      
      private String openid;
      
      private String client_id;
      
}

Create QQDTO to receive json parameters returned from QQ

import lombok.Data;

/**
 * description:  Yang Buyi website: www.yangbuyi.com top
 * program:  yangbuyi-erp-2020
 * ClassName:  QQDTO
 * create:  2020-06-24 17:20
 *
 * @author: yangbuyi
 * @since:  JDK1.8
 * @QQDTO: Used to store the parameters returned by QQ server
 **/

@Data
public class QQDTO {
      
      private String ret;        //Return code
      private String msg;        //If RET < 0, there will be a corresponding error message prompt, and all the returned data are encoded in UTF-8.
      private String nickname;             //User's nickname in QQ space.
      private String figureurl;              //Size 30 × 30 pixel QQ space avatar URL.
      private String figureurl_1;                //Size 50 × 50 pixel QQ space avatar URL.
      private String figureurl_2;                //Size is 100 × 100 pixel QQ space avatar URL.
      private String figureurl_qq_1;                   //Size 40 × 40 pixel QQ avatar URL.
      private String figureurl_qq_2;                   //Size is 100 × 100 pixel QQ avatar URL. It should be noted that not all users have QQ's 100x100 avatar, but 40x40 pixels will certainly have it.
      private String gender;           //Gender. If it is not available, it returns "male" by default
      private Integer gendertype; // Gender number
      private String is_yellow_vip;                  //Identify whether the user is a yellow diamond user (0: No; 1: Yes).
      private String vip;        //Identify whether the user is a yellow diamond user (0: No; 1: Yes)
      private String yellow_vip_level;                     //Yellow diamond grade
      private String level;          //Yellow diamond grade
      private String is_yellow_year_vip;                       //Identify whether it is an annual fee yellow diamond user (0: No; 1: Yes)
      private String province; // province
      private String city; // city
}

Example

Create front-end request jump controller

@Controller
@Slf4j
public class RequestController {

	  @RequestMapping("login")
	  public String login() {
			System.out.println("Log in");
			return "login";
	  }

	  @RequestMapping("home")
	  public String home() {
			return "home";
	  }

}

Create front page

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    Login address    action="/api/login/oauth"
-->
<form action="/api/login/oauth">
    <input type="submit" style="background: red;size: 25px" value="land">
</form>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="">
    <label class="">Login successful</label>
    <div class="">
        <p th:text="'openID :' + ${map.qqOpenidDTO.openid}"></p>

        <p th:text="'User name :' + ${map.user.nickname}"></p>

        User Avatar:
        <img th:src="${map.user.figureurl_qq_1}" alt="">
        <br>
        <img th:src="${map.user.figureurl_qq_1}" alt="">
        <img th:src="${map.user.figureurl_qq_2}" alt="">


        Gender:
        <p th:text="${map.user.gender}"></p>


        <p th:text="${map.user.vip}"></p>
        <p th:text="${map.user.yellow_vip_level}"></p>
        <p th:text="${map.user.is_yellow_year_vip}"></p>
        <p th:text="${map.user.province}"></p>
        <p th:text="${map.user.city}"></p>

    </div>
</div>

<!--parameter list:-->
<!--private String ret; //Return code -- >
<!--private String msg; //If RET < 0, there will be a corresponding error message prompt, and all the returned data are encoded in UTF-8. -- >
<!--private String nickname; //User's nickname in QQ space. -- >
<!--private String figureurl; //Size 30 × 30 pixel QQ space avatar URL. -- >
<!--private String figureurl_1; //Size 50 × 50 pixel QQ space avatar URL. -- >
<!--private String figureurl_2; //Size is 100 × 100 pixel QQ space avatar URL. -- >
<!--private String figureurl_qq_1; //Size 40 × 40 pixel QQ avatar URL. -- >
<!--private String figureurl_qq_2; //Size is 100 × 100 pixel QQ avatar URL. It should be noted that not all users have QQ's 100x100 avatar, but 40x40 pixels must be. - >
<!--private String gender; //Gender. If it cannot be obtained, it returns "male" - >
<!--private Integer gendertype; // Gender number -- >
<!--private String is_yellow_vip; //Identifies whether the user is a yellow diamond user (0: No; 1: Yes). -- >
<!--private String vip; //Identify whether the user is a yellow diamond user (0: No; 1: Yes) - >
<!--private String yellow_vip_level; //Yellow diamond grade -- >
<!--private String level; //Yellow diamond grade -- >
<!--private String is_yellow_year_vip; //Identify whether it is an annual yellow diamond user (0: No; 1: Yes) - >
<!--private String province; // Province -- >
<!--private String city; // City -- >


</body>
</html>

Startup precautions

It must be packaged to the server to start QQ before callback

Project deployment

Scheme I:

Click package to package

Copy project and application Upload YML to linux server

Modify application The port in YML is 80

Running Java programs

java -jar qqlogindemo-0.0.1-SNAPSHOT.jar

Start successful

Visit the login page

Click "login" QQ code scanning or "password login" to successfully jump to home

This is the end of QQ login from zero to third-party login.