• 第三方登录--QQ登录--单体应用


    从零玩转第三方QQ登录

    下面有源码

    前后端分离版本

    一样的思路

    https://www.cnblogs.com/Yangbuyi/p/13194007.html

    第三方GITEE登录

    https://www.cnblogs.com/Yangbuyi/p/yangbuyi.html

    oss对象存储超好玩

    https://www.cnblogs.com/Yangbuyi/p/13493845.html

    在真正开始对接之前,我们先来聊一聊后台的方案设计。既然是对接第三方登录,那就免不了如何将用户信息保存。首先需要明确一点的是,用户在第三方登录成功之后,
    我们能拿到的仅仅是一个代表用户唯一身份的ID(微博是真实uid,QQ是加密的openID)以及用来识别身份的accessToken,当然还有昵称、头像、性别等有限资料,
    对接第三方登录的关键就是如何确定用户是合法登录,如果确定这次登录的和上次登录的是同一个人并且不是假冒的。其实这个并不用我们特别操心,就以微博登录为例,
    用户登录成功之后会回调一个code给我们,然后我们再拿code去微博那换取 accessToken ,如果这个code是用户乱填的,那这一关肯定过不了,所以,前面的担心有点多余,哈哈。

    1. 认识Oauth2.0

    现在很多网站都要不管是为了引流也好,为了用户方便也好一般都有第三方账号登陆的需求,今天以QQ登陆为例,来实现一个最简单的第三方登陆。
    目前主流的第三方登录都是依赖的Oauth2.0实现的,最常见的就是在各种中小型网站或者App中的QQ登录,微信登录等等。所以我建议想要学习和实现第三方登录同学去了解下这个协议。

    1.2 必须要域名并且进行备案

    比如我的域名: https://yangbuyi.top/
    因为腾讯有一个域名认证机制啥的。。。。。。

    2.实名认证

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

    2.1、进行申请开发者身份

    2.2 创建应用

    进入应用管理页面创建应用,根据实际需要是创建网站应用还是移动应用,我这里是网站应用:

    提交成功完步后等待客服审核即可

    2.3. QQ登陆流程

    2.4. 请求参数

    3.前台准备

    创建 home.html login.html


    前端代码

    ## login.html

      <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    <form action="/api/login/oauth">
        <input type="submit" value="登陆">
    </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="">登陆成功</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="">
    ht
    
        </div>
    </div>
    
    </body>
    </html>		
    

    4. 后端实现

    ## pom依赖导入

      <!-- 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>
    

    yml配置文件

    server:
      port: 8080
    
    
    
    # QQ 登录 参数
    oauth:
      qq:
        #你的appid
        client_id: ????????
        #你的appkey
        client_secret: ????????
        #你接收响应code码地址
        redirect_uri: ????????
        #腾讯获取code码地址
        code_callback_uri: ????????
        #腾讯获取access_token地址
        access_token_callback_uri: ????????
        #腾讯获取openid地址
        openid_callback_uri: ????????
        #腾讯获取用户信息地址
        user_info_callback_uri: ????????
    
    spring:
      thymeleaf:
        cache: false
        suffix: .html
    
    

    创建HttpsUtils

    用于发送QQ服务器请求----请在gitee获取

    创建QQ包当中的所有文件----请在gitee获取

    QQDTO--- 用于存储QQ服务器返回来的参数

    QQOpenidDTO--- 用来存储 access_token、openid

    OAuthProperties--- 用于成为配置文件yml注入参数到QQProperties获取code码

    QQProperties--- 用于存储访问QQ服务器必要的参数

    5. 创建路由跳转

    RequestController

    @Controller
    @Slf4j
    public class RequestController {
    	 // 登陆
         @RequestMapping("login")
         public String login() {
             return "login";
         }
        
        
    	  // 登陆成功跳转
         @RequestMapping("home")
         public String home() {
             return "home";
         }
    
    }
    

    6.创建LoginController

    package top.yangbuyi.controller;
    
    
    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.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    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登陆接口
          *
          * @param response
          */
         @GetMapping("/login/oauth")
         public void loginQQ(HttpServletResponse response) {
             /**
              * 重定向
              */
             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();
             }
         }
    
     	 /**
    	   * 接收回调地址带过来的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
             /**
             遍历拿到的数据:
                access_token=6D45343586C39EAA1CFF016E081E4F3E
                refresh_token=3CD8AD92C146A6154AF89DD1DEEA86BB
                expires_in=7776000
             */
             Map<String, String> reulsts = getAccess_token(params);
           
             
    
             //到这里access_token已经处理好了
             //下一步获取openid,只有拿到openid才能拿到用户信息
             String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + "?access_token=" + reulsts.get("access_token"));
    
             System.out.println("openidContent: " + openidContent);
    
             //接下来对openid进行处理
             //截取需要的那部分json字符串
             String openid = openidContent.substring(openidContent.indexOf("{"), openidContent.indexOf("}") + 1);
             Gson gson = new Gson();
             //将返回的openid转换成DTO
             QQOpenidDTO qqOpenidDTO = gson.fromJson(openid, QQOpenidDTO.class);
    
             //接下来说说获取用户信息部分
             //登陆的时候去数据库查询用户数据对于openid是存在,如果存在的话,就不用拿openid获取用户信息了,而是直接从数据库拿用户数据直接认证用户,
             // 否则就拿openid去腾讯服务器获取用户信息,并存入数据库,再去认证用户
             //下面关于怎么获取用户信息,并登陆
             params.clear();
             params.put("access_token", reulsts.get("access_token"));//设置access_token
             params.put("openid", qqOpenidDTO.getOpenid());//设置openid
             params.put("oauth_consumer_key", qqOpenidDTO.getClient_id());//设置appid
             //获取用户信息
             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;
         }
    
    
    }
    

    7. 测试QQ登陆


    
    

    项目源代码--- 请移步GITee

    https://gitee.com/yangbuyi/bky_yby

  • 相关阅读:
    search
    longestValidParentheses
    nextPermutation
    linux下通过安装xampp搭建环境卸载xampp、以及网站搬家-64位系统Apache启动不了的问题
    对拍器 ADC-ACMDataCreater
    Educational Codeforces Round 86 (Rated for Div. 2) A~D
    Codeforces Round #637 (Div. 2) A~D
    Codeforces Round #636 (Div. 3) A~D
    Codeforces Round #635 (Div. 2) A~D
    原型设计工具比较及实践 基于WolframAlphaAPI的科学计算器原型设计
  • 原文地址:https://www.cnblogs.com/Yangbuyi/p/13524353.html
Copyright © 2020-2023  润新知