程序员scholar 程序员scholar
首页
  • Web 三剑客

    • HTML
    • CSS
    • JavaScript
  • 现代 JavaScript

    • ES6
    • TypeScript
  • 前端工具库

    • jQuery
    • Ajax
    • Axios
  • Vue 生态

    • Vue2
    • Vue3
    • Vue3 + TS
    • Vuex
  • 小程序开发

    • 微信小程序
    • uni-app
  • 构建工具

    • Webpack
  • 服务端技术

    • Node.js
  • 实时通信

    • WebSocket
    • 第三方登录
  • Element-UI
  • Apache ECharts
后端 (opens new window)
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Web 三剑客

    • HTML
    • CSS
    • JavaScript
  • 现代 JavaScript

    • ES6
    • TypeScript
  • 前端工具库

    • jQuery
    • Ajax
    • Axios
  • Vue 生态

    • Vue2
    • Vue3
    • Vue3 + TS
    • Vuex
  • 小程序开发

    • 微信小程序
    • uni-app
  • 构建工具

    • Webpack
  • 服务端技术

    • Node.js
  • 实时通信

    • WebSocket
    • 第三方登录
  • Element-UI
  • Apache ECharts
后端 (opens new window)
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • 第三方登录

    • JustAuth 基本使用
    • JustAuth 对接第三方登录
    • 原生QQ登录流程
      • Vue 2 中引入 SVG 图标
      • Vue3 vite引入 SVG 图标
      • Vue3 Vue CLI 中引入 SVG图标
    • 第三方登录
    • 第三方登录
    scholar
    2025-01-24
    目录

    原生QQ登录流程

    # 一、网站授权QQ登录

    • 首先需要去QQ互联申请应用
    • 填写网站的相关信息,以及回调地址,需要进行审核。
    • 申请流程暂时不说了,百度一下挺多申请失败案例的解决方案的,你懂的现在越来越严格了,甚至一个错别字都不让有。

    # 二、QQ登录的完整流程

    1. 用户点击QQ登录
      用户在你的前端页面点击QQ登录按钮,发送请求到后端。

    2. 重定向到QQ授权页面
      后端也可以直接重定向到QQ的授权页面,也可以将授权页面的地址返回给前端, 由前端将用户重定向到QQ的授权页面,授权页面的地址通常是一个URL,类似于:https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=YOUR_APP_ID&redirect_uri=YOUR_REDIRECT_URI&state=YOUR_STATE

      其中:

      • YOUR_APP_ID:你在QQ开放平台注册应用时获得的APP ID。
      • YOUR_REDIRECT_URI:你在QQ开放平台设置的回调URL。
      • YOUR_STATE:一个随机字符串,用于防止CSRF攻击。
    3. 用户授权
      用户在QQ的授权页面点击同意授权。

    4. QQ重定向到回调URL
      授权成功后,QQ会将用户重定向到你设置的redirectUri,并在回调地址的查询参数中加上一个code参数和原先的state。

    5. 前端获取code并验证state
      前端从回调地址中解析出code参数。同时,验证返回的state是否与最初发送的state一致,以确保这不是一个CSRF攻击。

      state参数也可以交给后端进行验证 这里的回调地址就是QQ互联上面写的回调地址,前端会在这个回调地址的页面发送请求给后端,同时携带code和state参数(这两个参数从回调地址里面取出来的)

    6. 前端向后端发送登录请求并携带code和state参数
      前端发起请求,将code和state发送到后端的/callback接口。

    7. 后端获取Access Token
      后端使用code,向QQ服务器请求访问令牌(Access Token)。这通常涉及到一个POST请求到https://graph.qq.com/oauth2.0/token,带有code、YOUR_APP_ID、YOUR_APP_KEY(你的应用密钥)和YOUR_REDIRECT_URI作为参数。

    8. 后端获取OpenID
      使用Access Token,后端可以向QQ服务器请求OpenID,这是一个代表QQ用户唯一标识的值。

    9. 后端获取用户信息
      后端使用Access Token和OpenID,请求QQ服务器以获取用户的基本信息。

    10. 创建或登录用户
      后端可以使用从QQ获取的用户信息来:

      • 检查数据库中是否已经有一个与此QQ账户关联的用户。
      • 如果是,则登录该用户。
      • 如果不是,则创建一个新用户,并与该QQ账户关联。
    11. 返回结果到前端
      后端可以返回一个令牌(如JWT)或其他标识已登录用户的信息到前端。

    12. 前端处理登录状态
      前端根据后端的响应处理用户的登录状态,例如保存JWT,显示用户的信息等。

    # 三、代码示例

    # (1)添加依赖

    在pom.xml中添加相关的依赖:

    <!-- Spring Boot Web Starter: 提供了创建web应用所需要的所有必要依赖,包括内嵌的Tomcat服务器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Apache HttpClient: 一个流行的库,用于处理HTTP请求 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # (2)配置文件

    在application.properties添加:

    qq.appId=YOUR_APP_ID
    qq.appKey=YOUR_APP_KEY
    qq.redirectUri=http://yourdomain.com/auth/qq/callback
    
    1
    2
    3

    # (3)QQ实体类

    /**
     * 定义一个表示QQ用户信息的数据传输对象(DTO)
     */
    @Data
    public class QQInfoDto {
    
        private Integer ret;              // 返回码,用于表示请求的结果状态
        private String msg;               // 返回的消息内容,用于描述请求结果"
        private String nickname;          // QQ用户的昵称
        private String figureurl_qq_1;    // QQ用户的头像URL (40x40像素的小尺寸头像)
        private String figureurl_qq_2;    // QQ用户的另一个头像URL (100x100像素的大尺寸头像)
        private String gender;            // QQ用户的性别
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # (4)JSON工具类

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.List;
    
    /**
     * JSON工具类
     */
    public class JsonUtils {
    
        // 使用SLF4J创建日志记录器
        private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
    
        /**
         * 将对象转换为JSON字符串
         *
         * @param obj 需要转换的对象
         * @return 转换后的JSON字符串
         */
        public static String convertObj2Json(Object obj) {
            return JSON.toJSONString(obj);
        }
    
        /**
         * 将JSON字符串转换为指定类型的对象
         *
         * @param json  要转换的JSON字符串
         * @param classz 指定的返回对象类型
         * @param <T> 泛型类型
         * @return 返回转换后的对象
         */
        public static <T> T convertJson2Obj(String json, Class<T> classz) {
            return JSONObject.parseObject(json, classz);
        }
    
        /**
         * 将JSON数组字符串转换为List集合
         *
         * @param json   要转换的JSON数组字符串
         * @param classz 集合中对象的类型
         * @param <T>  泛型类型
         * @return 返回转换后的List集合
         */
        public static <T> List<T> convertJsonArray2List(String json, Class<T> classz) {
            return JSONArray.parseArray(json, classz);
        }
    
        // 测试方法
        public static void main(String[] args) {
        }
    }
    
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56

    # (5)实现Service

    创建一个QQAuthService用于处理与QQ的交互。

    @Service  
    public class QQAuthService {
    
        // 从application.properties中读取配置值
        @Value("${qq.appId}")
        private String appId;
    
        @Value("${qq.appKey}")
        private String appKey;
    
        @Value("${qq.redirectUri}")
        private String redirectUri;
    
        // 创建一个可关闭的HTTP客户端,用于发送请求
        private final CloseableHttpClient httpClient = HttpClients.createDefault();
    
        /**
         * 获取Access Token
         * 
         * @param code 从QQ回调URL中获得的授权码
         * @return 返回Access Token
         * @throws IOException 处理HTTP请求可能会出现的IO异常
         */
        public String getAccessToken(String code) throws IOException {
            // 构建获取Access Token的URL
            String url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=" 
                + appId + "&client_secret=" + appKey + "&code=" + code + "&redirect_uri=" + redirectUri;
            
            // 创建一个HttpGet请求
            HttpGet httpGet = new HttpGet(url);
            // 执行该请求,并获取响应
            CloseableHttpResponse response = httpClient.execute(httpGet);
            // 将响应内容转换为字符串
            String responseStr = EntityUtils.toString(response.getEntity());
            // 解析响应内容,提取access_token
            String accessToken = responseStr.split("&")[0].split("=")[1];
            return accessToken;
        }
    
        /**
         * 获取OpenID
         * 
         * @param accessToken Access Token
         * @return 返回OpenID
         * @throws IOException 处理HTTP请求可能会出现的IO异常
         */
        public String getOpenId(String accessToken) throws IOException {
            // 构建获取OpenID的URL
            String url = "https://graph.qq.com/oauth2.0/me?access_token=" + accessToken;
            
            // 创建一个HttpGet请求
            HttpGet httpGet = new HttpGet(url);
            // 执行该请求,并获取响应
            CloseableHttpResponse response = httpClient.execute(httpGet);
            // 将响应内容转换为字符串
            String responseStr = EntityUtils.toString(response.getEntity());
            // 解析响应内容,提取openid
            String openId = responseStr.substring(responseStr.lastIndexOf(":\"") + 2, responseStr.lastIndexOf("\"}"));
            return openId;
        }
    
        /**
         * 获取用户信息
         * 
         * @param accessToken Access Token
         * @param openId OpenID
         * @return 返回含有用户信息的对象
         * @throws IOException 处理HTTP请求可能会出现的IO异常
         */
        public QQInfoDto getUserInfo(String accessToken, String openId) throws IOException {
            // 构建获取用户信息的URL
            String url = "https://graph.qq.com/user/get_user_info?access_token=" + accessToken + "&oauth_consumer_key=" 
                + appId + "&openid=" + openId;
            
            // 创建一个HttpGet请求
            HttpGet httpGet = new HttpGet(url);
            // 执行该请求,并获取响应
            CloseableHttpResponse response = httpClient.execute(httpGet);
            // 将响应内容转换为字符串
            String responseStr = EntityUtils.toString(response.getEntity());
            // 将响应字符串转化为QQInfoDto对象
            QQInfoDto qqInfo = JsonUtils.convertJson2Obj(responseStr, QQInfoDto.class);
            // 直接返回含有用户信息的对象
            return qqInfo;  
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87

    # (6)创建Controller

    @RestController
    @RequestMapping("/auth/qq")
    public class QQAuthController {
    
        @Autowired
        private QQAuthService qqAuthService;
    
        /**
         * 重定向用户到QQ登录页面。
         * 
         * @param response HttpServletResponse对象,用于发送重定向。
         * @param session  HttpSession对象,用于存储state。
         * @throws IOException 如果重定向失败。
         */
        @GetMapping("/login")
        public void qqLogin(HttpServletResponse response, HttpSession session) throws IOException {
            // 生成并存储一个唯一的state值
            String state = UUID.randomUUID().toString();
            session.setAttribute("qq_oauth_state", state);
    
            // 构建QQ授权的URL
            String url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" 
                + appId + "&redirect_uri=" + URLEncoder.encode(redirectUri, "UTF-8") + "&state=" + state;
            
            // 当你在移动端上请求登录时,可以在请求中带上display=mobile参数
            // 系统就会跳转到移动版的QQ登录页面,为用户提供更好的体验
            if ("mobile".equals(display)) {
                url += "&display=mobile";
            }
            // 重定向到QQ授权页面
            response.sendRedirect(url);
        }
    
        /**
         * 处理QQ授权的回调请求。
         * 
         * @param code QQ授权返回的code。
         * @param state QQ授权返回的state。
         * @param session HttpSession对象,用于验证state。
         * @return 用户信息或错误消息。
         */
        @GetMapping("/callback")
        public String qqCallback(@RequestParam String code, @RequestParam String state, HttpSession session) {
            // 检查返回的state是否与存储的一致
            String storedState = (String) session.getAttribute("qq_oauth_state");
            if (storedState == null || !storedState.equals(state)) {
                return "Error: state does not match";
            }
    
            try {
                // 获取并使用AccessToken和OpenID
                String accessToken = qqAuthService.getAccessToken(code);
                String openId = qqAuthService.getOpenId(accessToken);
                QQUserInfo userInfo = qqAuthService.getUserInfo(accessToken, openId);
    
                // 这里可以进行用户注册或登录操作
                // 这里可以根据用户是新用户还是老用户决定是注册后登录还是直接登录
                // 然后返回登录用户的信息
                return userInfo;
            } catch (IOException e) {
                return "Error occurred during QQ auth: " + e.getMessage();
            }
        }
    }
    
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    编辑此页 (opens new window)
    JustAuth 对接第三方登录
    Vue 2 中引入 SVG 图标

    ← JustAuth 对接第三方登录 Vue 2 中引入 SVG 图标→

    Theme by Vdoing | Copyright © 2019-2025 程序员scholar
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式