前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零玩转第三方登录之QQ登录

从零玩转第三方登录之QQ登录

原创
作者头像
杨不易呀
修改2022-01-20 10:26:55
1.3K0
修改2022-01-20 10:26:55
举报
文章被收录于专栏:杨不易呀杨不易呀

从零玩转第三方登录之QQ登录

前言

在真正开始对接之前,我们先来聊一聊后台的方案设计。既然是对接第三方登录,那就免不了如何将用户信息保存。首先需要明确一点的是,用户在第三方登录成功之后,

我们能拿到的仅仅是一个代表用户唯一身份的ID(微博是真实uid,QQ是加密的openID)以及用来识别身份的accessToken,当然还有昵称、头像、性别等有限资料,

对接第三方登录的关键就是如何确定用户是合法登录,如果确定这次登录的和上次登录的是同一个人并且不是假冒的。其实这个并不用我们特别操心,就以微博登录为例,

用户登录成功之后会回调一个code给我们,然后我们再拿code去微博那换取 accessToken ,如果这个code是用户乱填的,那这一关肯定过不了,所以,前面的担心有点多余,哈哈。

准备工作

1、前往腾讯云注册域名

2、购买腾讯云服务器用于qq登录

1. 认识Oauth2.0

现在很多网站都要不管是为了引流也好,为了用户方便也好一般都有第三方账号登陆的需求,今天以QQ登陆为例,来实现一个最简单的第三方登陆。

目前主流的第三方登录都是依赖的Oauth2.0实现的,最常见的就是在各种中小型网站或者App中的QQ登录,微信登录等等。所以我建议想要学习和实现第三方登录同学去了解下这个协议。

必须要域名并且进行备案

比如我的域名: https://yangbuyi.top/

因为腾讯有一个域名认证机制啥的。。。。。。

2.实名认证

QQ登录我们对接的是QQ互联,地址:https://connect.qq.com ,首先需要注册成为开发者并实名认证,需要手持身份证照片,具体就不讲了。

2.1、进行申请开发者身份

image
image

2.2 创建应用

进入应用管理页面创建应用,根据实际需要是创建网站应用还是移动应用,我这里是网站应用:
image
image
image
image
提交成功完步后等待客服审核即可
image
image
这是我网站的基本接口信息
image
image

QQ登陆流程

image
image
请求参数
image
image

创建springboot工程

image
image
依赖
代码语言:java
复制
        <!-- qq登陆集成 开始 -->
        <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转换工具-->
        <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登陆集成 结束 -->


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


        <!-- 其它配置 -->
        <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>

创建http请求工具

代码语言:java
复制
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:  杨不易网站 :www.yangbuyi.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 {
            // 设置连接池
            connMgr = new PoolingHttpClientConnectionManager();
            // 设置连接池大小
            connMgr.setMaxTotal(100);
            connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
            // Validate connections after 1 sec of inactivity
            connMgr.setValidateAfterInactivity(1000);
            RequestConfig.Builder configBuilder = RequestConfig.custom();
            // 设置连接超时
            configBuilder.setConnectTimeout(MAX_TIMEOUT);
            // 设置读取超时
            configBuilder.setSocketTimeout(MAX_TIMEOUT);
            // 设置从连接池获取连接实例的超时
            configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
            
            requestConfig = configBuilder.build();
      }
      
      /**
       * 发送 GET 请求(HTTP),不带输入数据
       *
       * @param url
       * @return
       */
      public static String doGet(String url) {
            return doGet(url, new HashMap<String, Object>());
      }
      
      /**
       * 发送 GET 请求(HTTP),K-V形式
       *
       * @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;
      }
      
      /**
       * 发送 POST 请求(HTTP),不带输入数据
       *
       * @param apiUrl
       * @return
       */
      public static String doPost(String apiUrl) {
            return doPost(apiUrl, new HashMap<String, Object>());
      }
      
      /**
       * 发送 POST 请求,K-V形式
       *
       * @param apiUrl API接口URL
       * @param params 参数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;
      }
      
      /**
       * 发送 POST 请求,JSON形式
       *
       * @param apiUrl
       * @param json   json对象
       * @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");// 解决中文乱码问题
                  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;
      }
      
      /**
       * 创建SSL安全连接
       *
       * @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开始*/
      /**
       * 发送get请求,利用java代码发送请求
       * @param url
       * @return
       * @throws Exception
       */
      public static String doGetHub(String url) throws Exception{
            
            CloseableHttpClient httpclient = HttpClients.createDefault();
            
            HttpGet httpGet = new HttpGet(url);
            // 发送了一个http请求
            CloseableHttpResponse response = httpclient.execute(httpGet);
            // 如果响应200成功,解析响应结果
            if(response.getStatusLine().getStatusCode()==200){
                  // 获取响应的内容
                  HttpEntity responseEntity = response.getEntity();
                  
                  return EntityUtils.toString(responseEntity);
            }
            return null;
      }
      /**
       * 将字符串转换成map
       * @param responseEntity
       * @return
       */
      public static Map<String,String> getMap(String responseEntity) {
            
            Map<String, String> map = new HashMap<>();
            // 以&来解析字符串
            String[] result = responseEntity.split("\\&");
            
            for (String str : result) {
                  // 以=来解析字符串
                  String[] split = str.split("=");
                  // 将字符串存入map中
                  if (split.length == 1) {
                        map.put(split[0], null);
                  } else {
                        map.put(split[0], split[1]);
                  }
                  
            }
            return map;
      }
      
      /**
       * 通过json获得map
       * @param responseEntity
       * @return
       */
      public static Map<String,String> getMapByJson(String responseEntity) {
            Map<String, String> map = new HashMap<>();
            // 阿里巴巴fastjson  将json转换成map
            JSONObject jsonObject = JSONObject.parseObject(responseEntity);
            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                  String key = entry.getKey();
                  // 将obj转换成string
                  String value = String.valueOf(entry.getValue()) ;
                  map.put(key, value);
            }
            return map;
      }
      /*gitHub结束*/
      
      
}

创建跨域配置类 以防万一出现跨域问题

代码语言:java
复制
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("*");
        // 表示什么域名跨域 *表示全部都跨域
        corsConfiguration.addAllowedOrigin("*");
        // 注入进去
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);

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

创建Logincontroller

代码语言:java
复制
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: 杨不易网站:www.yangbuyi.top
 * @program: qqlogindemo
 * @ClassName: loginController
 * @create: 2020-08-18 14:41
 * @author: yangbuyi
 * @since: JDK1.8
 * @loginController: 第三方QQ登陆
 **/

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

	  /**
	   * 认证参数
	   */
	  @Autowired
	  private OAuthProperties oauth;


	  /**
	   * 调用QQ登陆接口
	   * 流程: 先调用接口获取code,在根据code获取access_token,在根据token获取对应的用户信息
	   * @param response
	   */
	  @GetMapping("/login/oauth")
	  public void loginQQ(  HttpServletResponse response) {
			// 重定向访问QQ登录服务器
			try {
				  response.sendRedirect(oauth.getQQ().getCode_callback_uri() + //获取code码地址
						  "?client_id=" + oauth.getQQ().getClient_id() //appid
						  +"&state=" + UUID.randomUUID() + //这个说是防攻击的,就给个随机uuid吧
						  "&redirect_uri=" + oauth.getQQ().getRedirect_uri() +//这个很重要,这个是回调地址,即就收腾讯返回的code码
						  "&response_type=code");
			} catch (IOException e) {
				  e.printStackTrace();
			}
	  }


	  /**
	   * 在qq平台设置的回调地址
	   *
	   * 接收回调地址带过来的code码
	   *
	   * @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());

			// 获取腾讯access token
			Map<String, String> reulsts = getAccess_token(params);
			System.out.println("遍历拿到的数据:");
			for (Map.Entry<String, String> entry : reulsts.entrySet()) {
				  System.out.println(entry.getKey() + "=" + entry.getValue());
			}
			System.out.println("遍历完毕");

			//到这里access_token已经处理好了
			//下一步获取openid,只有拿到openid才能拿到用户信息
			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);

			//接下来对openid进行处理
			//截取需要的那部分json字符串
			String openid = openidContent.substring(openidContent.indexOf("{"), openidContent.indexOf("}") + 1);
			// json 转 对象
			Gson gson = new Gson();
			//将返回的openid转换成DTO
			QQOpenidDTO qqOpenidDTO = gson.fromJson(openid, QQOpenidDTO.class);

			// 封装参数 请求用户信息数据
			params.clear();
			//设置access_token
			params.put("access_token", reulsts.get("access_token"));
			//设置openid
			params.put("openid", qqOpenidDTO.getOpenid());
			//设置appid
			params.put("oauth_consumer_key", qqOpenidDTO.getClient_id());
			//获取用户信息
			String userInfo = HttpsUtils.doGet(oauth.getQQ().getUser_info_callback_uri(), params);
			QQDTO qqDTO = gson.fromJson(userInfo, QQDTO.class);
			// (正常情况下,在开发时候用openid作为用户名,再自己定义个密码就可以了)
			try {

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


	  /**
	   * 获取腾讯 access_token
	   *
	   * @return
	   */
	  public Map<String, String> getAccess_token(HashMap<String, Object> params) {
			// 认证地址
			//获取access_token如:access_token=9724892714FDF1E3ED5A4C6D074AF9CB&expires_in=7776000&refresh_token=9E0DE422742ACCAB629A54B3BFEC61FF
			String result = HttpsUtils.doGet(oauth.getQQ().getAccess_token_callback_uri(), params);
			//对拿到的数据进行切割字符串
			String[] strings = result.split("&");
			//切割好后放进map
			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;
	  }


}

创建QQ参数实体类

创建 OAuthProperties 用于配合yml配置文件动态获取参数
代码语言:java
复制
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * description:  杨不易网站 :www.yangbuyi.top
 * ClassName:  OAuthProperties
 * create:  2020-06-24 17:06
 *
 * @author: yangbuyi
 * @since: JDK1.8
 * <p>
 * 获取Code码
 **/

@Component
//对应application.yml中,oauth下参数
@ConfigurationProperties(prefix = "oauth")
public class OAuthProperties {
      
      //获取applicaiton.yml下qq下所有的参数
      private QQProperties qq = new QQProperties();
      
      public QQProperties getQQ() {
            return qq;
      }
      
      public void setQQ(QQProperties qq) {
            this.qq = qq;
      }
}
创建 QQProperties 用于请求qq的参数
代码语言:java
复制
import lombok.Data;
import org.springframework.stereotype.Component;

/**
 * description:  杨不易网站 :www.yangbuyi.top
 * ClassName:  QQProperties
 * create:  2020-06-24 17:04
 *
 * @author: yangbuyi
 * @since: JDK1.8
 *
 * 集成第三方登陆 QQ  参数
 **/
@Data
@Component
public class QQProperties {
      /**
       * 你的appid
       */
      private String client_id;
      /**
       *  #你的appkey
       */
      private String client_secret;
      /**
       * 你接收响应code码地址
       */
      private String redirect_uri;
      /**
       * 腾讯获取code码地址
       */
      private String code_callback_uri;
      /**
       * 腾讯获取access_token地址
       */
      private String access_token_callback_uri;
      /**
       * 腾讯获取openid地址
       */
      private String openid_callback_uri;
      /**
       * 腾讯获取用户信息地址
       */
      private String user_info_callback_uri;
      
      
      /**
       * 要回调到哪个网站
       */
      private String redirect_url_index_yby;
      private String redirect_url_login_yby;
    
}

创建 QQOpenidDTO 用于获取 access_token、openid

代码语言:java
复制
import lombok.Data;

/**
 * description:  杨不易网站 :www.yangbuyi.top
 * ClassName:  QQOpenidDTO
 * create:  2020-06-24 17:19
 *
 * @author: yangbuyi
 * @since: JDK1.8
 *
 * 用来获取 access_token、openid
 **/
@Data

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

创建QQDTO 接收QQ返回来的json参数

代码语言:java
复制
import lombok.Data;

/**
 * description:  杨不易网站 :www.yangbuyi.top
 * program:  yangbuyi-erp-2020
 * ClassName:  QQDTO
 * create:  2020-06-24 17:20
 *
 * @author: yangbuyi
 * @since: JDK1.8
 * @QQDTO: 用于存储QQ服务器返回来的参数
 **/

@Data
public class QQDTO {
      
      private String ret;        //返回码
      private String msg;        //如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
      private String nickname;             //用户在QQ空间的昵称。
      private String figureurl;              //大小为30×30像素的QQ空间头像URL。
      private String figureurl_1;                //大小为50×50像素的QQ空间头像URL。
      private String figureurl_2;                //大小为100×100像素的QQ空间头像URL。
      private String figureurl_qq_1;                   //大小为40×40像素的QQ头像URL。
      private String figureurl_qq_2;                   //大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。
      private String gender;           //性别。 如果获取不到则默认返回"男"
      private Integer gendertype; // 性别 数字
      private String is_yellow_vip;                  //标识用户是否为黄钻用户(0:不是;1:是)。
      private String vip;        //标识用户是否为黄钻用户(0:不是;1:是)
      private String yellow_vip_level;                     //黄钻等级
      private String level;          //黄钻等级
      private String is_yellow_year_vip;                       //标识是否为年费黄钻用户(0:不是; 1:是)
      private String province; // 省
      private String city; // 市
}

示例

image
image

创建前端请求跳转 controller

代码语言:java
复制
@Controller
@Slf4j
public class RequestController {

	  @RequestMapping("login")
	  public String login() {
			System.out.println("登陆进来啦");
			return "login";
	  }

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

}

创建前端页面

image
image
login.html
代码语言:html
复制
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    登录地址    action="/api/login/oauth"
-->
<form action="/api/login/oauth">
    <input type="submit" style="background: red;size: 25px" value="登陆">
</form>
</body>
</html>
home.html
代码语言: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="">登陆成功</label>
    <div class="">
        <p th:text="'openID :' + ${map.qqOpenidDTO.openid}"></p>

        <p th:text="'用户名称 :' + ${map.user.nickname}"></p>

        用户头像:
        <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="">


        性别:
        <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>

<!--参数列表:-->
<!--private String ret; //返回码-->
<!--private String msg; //如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。-->
<!--private String nickname; //用户在QQ空间的昵称。-->
<!--private String figureurl; //大小为30×30像素的QQ空间头像URL。-->
<!--private String figureurl_1; //大小为50×50像素的QQ空间头像URL。-->
<!--private String figureurl_2; //大小为100×100像素的QQ空间头像URL。-->
<!--private String figureurl_qq_1; //大小为40×40像素的QQ头像URL。-->
<!--private String figureurl_qq_2; //大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。-->
<!--private String gender; //性别。 如果获取不到则默认返回"男"-->
<!--private Integer gendertype; // 性别 数字-->
<!--private String is_yellow_vip; //标识用户是否为黄钻用户(0:不是;1:是)。-->
<!--private String vip; //标识用户是否为黄钻用户(0:不是;1:是)-->
<!--private String yellow_vip_level; //黄钻等级-->
<!--private String level; //黄钻等级-->
<!--private String is_yellow_year_vip; //标识是否为年费黄钻用户(0:不是; 1:是)-->
<!--private String province; // 省-->
<!--private String city; // 市-->


</body>
</html>

启动注意事项

必须要打包到服务器启动QQ才能回调

项目部署

方案一:

点击package 打包

image
image

复制 项目 和 application.yml 上传到linux服务器

image
image
image
image

修改application.yml 中的端口为 80

image
image

运行 Java程序

java -jar qqlogindemo-0.0.1-SNAPSHOT.jar

启动成功

image
image

访问 login 页面

image
image

点击登录 》 QQ扫码或者密码登录 》 登录成功 跳转到 home

image
image
image
image
image
image

到此 从零玩转 第三方登录之QQ登录 就结束了哦。

GITEE:https://gitee.com/yangbuyi

GITHUB: https://github.com/yangbuyiya

个人博客网站: https://www.yangbuyi.top/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从零玩转第三方登录之QQ登录
    • 前言
      • 准备工作
      • 1. 认识Oauth2.0
        • 必须要域名并且进行备案
          • 2.1、进行申请开发者身份
            • 2.2 创建应用
            • QQ登陆流程
            • 创建springboot工程
              • 创建http请求工具
                • 创建跨域配置类 以防万一出现跨域问题
                  • 创建Logincontroller
                    • 创建QQ参数实体类
                      • 创建 QQOpenidDTO 用于获取 access_token、openid
                        • 创建QQDTO 接收QQ返回来的json参数
                          • 创建前端请求跳转 controller
                            • 创建前端页面
                              • 启动注意事项
                              • 项目部署
                                • 到此 从零玩转 第三方登录之QQ登录 就结束了哦。
                                  • GITEE:https://gitee.com/yangbuyi
                                    • GITHUB: https://github.com/yangbuyiya
                                      • 个人博客网站: https://www.yangbuyi.top/
                                      相关产品与服务
                                      云服务器
                                      云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档