Spring Boot开发之使用JustAuth组件实现第三方登录(QQ、微博等)

在我们的项目开发中,使用第三方登录(如QQ登录、微信登录等)可以更加方便、轻松地实现用户登录。

在以往的开发过程中,如果要使项目实现第三方登录功能,一般过程是阅读官网的开发文档,并下载其JDK(或者依赖pom),然后进行开发实现。

但是,如果网站要实现多个第三方平台的登录功能,则需要很高的学习成本

所以,就有开发者实现了一款基于Spring Boot的开箱即用的整合第三方登录的开源组件:JustAuth

该插件的网址:https://justauth.wiki

本文将基于Spring Boot,使用JustAuth组件实现第三方快捷登录,并获取用户的uid。

1. 插件简介

首先给出几个链接:

在组件的各个网址,都可以看到关于该组件的自述:

小而全而美的第三方登录开源组件。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐、Gitlab、美团、饿了么和推特等第三方平台的授权登录。 Login, so easy!

可以看出,此组件支持的第三方登录平台,是非常全的。

下面,以QQ微博为例,测试Spring Boot上使用该组件完成第三方登录快速开发。

2. 项目创建与导包

关于Spring Boot项目如何创建,可参考以下几篇文章:

只看前两个就可以了。

创建完成项目之后,在pom.xml文件中,导入依赖:

        <!--JustAuth-->
        <dependency>
            <groupId>me.zhyd.oauth</groupId>
            <artifactId>JustAuth</artifactId>
            <version>1.15.9</version>
        </dependency>
        <!--http请求相关-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
            <version>5.3.9</version>
        </dependency>
        <!--导入lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

这里导入了4个依赖,说明如下:

第一个是JustAuth组件必须的,版本就默认使用帮助文档中的最新版,如下图所示。

image-20210324110815800

第二个是http请求的实现组件,这是因为JustAuth从 v1.14.0开始不会默认集成hutool-http,需要单独添加;

第三个是lombok,即自动生成模型的setget。因为项目中授权登录成功后,返回的用户信息模型,使用了@Getter@Setter注解。添加此组件,方便获取用户的单个信息(如uuid、nickname等)。

image-20210324145321900

第四个是阿里巴巴的fastjson 组件,因为在返回的结果中,有些信息是封装成 JSON格式 的,需要使用该组件来转换成 map键值对 ,从而获取对我们有用的信息。

image-20210324145458266

3. QQ登录实现

1、 新建一个名为 PluginController 的控制类,用来实现第三方插件的登录回调功能。

package com.demo.controller;

import org.springframework.web.bind.annotation.*;

/**
 * Info:登录组件测试
 *
 * @author: 拾年之璐
 * @date: 2021-03-15 0015 19:27
 */
@RestController
@RequestMapping(value = "/plugin")
public class PluginController {
    
    
    
}

2、创建一个AuthRequest接口的实现类,用于生成登录链接登录并获取用户信息

    /**
     * 授权接口类
     *
     * @return 各种请求的结果
     */
    private AuthRequest getQQAuthRequest() {
    
    
        return new AuthQqRequest(AuthConfig.builder()
                .clientId("APPID")
                .clientSecret("Your APP Secret")
                .redirectUri("http://XXX.com/plugin/qqlogin/callback")
                .unionId(true) //如果获取用户的UnionID,则设置为true
                .build());
    }

3、生成登录链接跳转至登录页面

    /**
     * 跳转至登录页面
     *
     * @param response 页面跳转
     * @throws IOException
     */
    @RequestMapping("/qqlogin")
    public void qqlogin(HttpServletResponse response) throws IOException {
    
    
        //获取对象
        AuthRequest authRequest = getQQAuthRequest();
        //打印生成的链接
        System.out.println("生成登录链接:" + authRequest.authorize("yourState"));
        //页面跳转,其中state参数可自定义
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }

这时,控制台可以打印出生成的登录URL

image-20210324145749836

浏览器访问http://XXX.com/plugin/qqlogin,即可跳转至此登录页面

image-20210324145949261

4、回调页面plugin/qqlogin/callback获取返回的code,并通过返回的code获取当前登录用户的信息

如下:

   /**
     * QQ登录成功后返回到此页
     *
     * @param callback 登录用户的信息
     * @return
     */
    @RequestMapping("/qqlogin/callback")
    public Object qqloginCallback(AuthCallback callback) {
    
    
        AuthRequest authRequest = getQQAuthRequest();
        // 打印返回的授权信息(QQ登录为code,通过code获取用户信息)
        System.out.println(callback.getCode());

        //根据返回的参数,执行登录请求(获取用户信息)
        AuthResponse<AuthUser> authResponse = authRequest.login(callback);

        // 将JSON包返回到前端页面显示
        return authResponse;
    }

上面的图,点击登录后,即可跳转至回调页面,显示出所有的JSON数据,如下图所示:

image-20210324150515950

我们对该JSON数据进行分析:

  • 返回的code是2000,表示获取数据成功(其他代码,可以在https://justauth.wiki/quickstart/error_code.html查看);
  • 返回的data数据,包含所有有用的信息,如用户IDuuid、用户昵称username、头像avatar、性别gender等等;
  • data下的token,包含所有的获取token刷新token过期时间等信息;
  • data下rawUserInfo,是第三方平台返回的原始用户信息
  • 关于返回的JSON数据的每个子段的详细含义,都可以在模型AuthUser中查看。

5、后台获取指定信息

上面可以获取所有的用户信息,但这些信息,我们并不能这样使用,而是筛选有用的信息,然后保存到自己的数据库。

所以下面这个完整示例,演示如何获取上述JSON中的指定信息。

  /**
     * QQ登录成功后返回到此页
     *
     * @param callback 登录用户的信息
     * @return
     */
    @RequestMapping("/qqlogin/callback")
    public Object qqloginCallback(AuthCallback callback) {
    
    
        //获取实例
        AuthRequest authRequest = getQQAuthRequest();
        // 打印返回的授权信息(QQ登录为code,通过code获取用户信息)
        System.out.println(callback.getCode());
        //根据返回的参数,执行登录请求(获取用户信息)
        AuthResponse<AuthUser> authResponse = authRequest.login(callback);

        //打印授权返回代码(2000 表示成功,可以用来判断用户登录成功与否)
        System.out.println("状态码:"+authResponse.getCode());

        //打印用户的昵称、ID、头像等基本信息
        System.out.println("用户的UnionID:" + authResponse.getData().getUuid());
        System.out.println("用户的昵称:" + authResponse.getData().getNickname());
        System.out.println("用户的头像:" + authResponse.getData().getAvatar());

        //打印用户的Token中的信息
        System.out.println("access_token:" + authResponse.getData().getToken().getAccessToken());
        System.out.println("用户的OpenId:" + authResponse.getData().getToken().getOpenId());

        // 打印更加详细的信息(第三方平台返回的原始用户信息)
        //getInnerMap():将JSONObject转换成Map键值对
        System.out.println("用户的城市:" + authResponse.getData().getRawUserInfo().getInnerMap().get("city"));
        System.out.println("用户的年份:" + authResponse.getData().getRawUserInfo().getInnerMap().get("year"));

        // 将JSON包返回到前端页面显示
        return authResponse;
    }

其实这段代码,和JSON数据,是一一对应的,如下图所示:

image-20210324152254531

打印结果:

image-20210324152337935

4. 微博登录实现

有了上面QQ登录的详细分析,微博登录也是类似的,这里速战速决。

1、创建授权请求类

    /**
     * 生成微博授权登录的类getWbAuthRequest
     *
     * @return
     */
    private AuthRequest getWbAuthRequest() {
    
    
        return new AuthWeiboRequest(AuthConfig.builder()
                .clientId("appid")
                .clientSecret("app secret")
                .redirectUri("http://xxx.com/plugin/sinalogin/callback")
                .build());
    }

2、生成并跳转登录链接

   /**
     * 微博登录页面,生成登录链接
     *
     * @param response 页面跳转
     * @throws IOException
     */
    @RequestMapping("/wblogin")
    public void wblogin(HttpServletResponse response) throws IOException {
    
    
        //生成对象
        AuthRequest authRequest = getWbAuthRequest();
        //生成链接并返回,其中state可以自定义,可以用来实现第四方登录
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }

结果:

image-20210324153313077

3、回调页面获取信息,以及返回JSON数据

    /**
     * 回调页面,获取登录用户新
     *
     * @param callback 详细JSON信息
     * @return
     */
    @RequestMapping("/sinalogin/callback")
    public Object wbloginCallback(AuthCallback callback) {
    
    
        //生成对象
        AuthRequest authRequest = getWbAuthRequest();
        //获取登录后返回的用户信息结果
        AuthResponse<AuthUser> authResponse = authRequest.login(callback);

        //打印授权返回代码(2000 表示成功,可以用来判断用户登录成功与否)
        System.out.println("状态码:" + authResponse.getCode());

        //打印用户的昵称、ID、头像等基本信息
        System.out.println("用户的UnionID:" + authResponse.getData().getUuid());
        System.out.println("用户的昵称:" + authResponse.getData().getNickname());
        System.out.println("用户的头像:" + authResponse.getData().getAvatar());

        //打印用户的Token中的信息
        System.out.println("access_token:" + authResponse.getData().getToken().getAccessToken());
        System.out.println("用户的OpenId:" + authResponse.getData().getToken().getOpenId());

        // 打印更加详细的信息(第三方平台返回的原始用户信息)
        System.out.println("用户的城市:" + authResponse.getData().getRawUserInfo().getInnerMap().get("city"));
        System.out.println("用户的年份:" + authResponse.getData().getRawUserInfo().getInnerMap().get("year"));
        //返回JSON信息
        return authResponse;
    }

结果:

image-20210324153839118

5. 完整Controller源码

package com.demo.controller;

import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthWeiboRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Info:登录组件测试
 *
 * @author: 拾年之璐
 * @date: 2021-03-22 0022 19:31
 */
@RestController
@RequestMapping(value = "/plugin")
public class PluginController {
    
    

    /**
     * QQ登录request类
     *
     * @return 链接
     */
    private AuthRequest getQQAuthRequest() {
    
    
        return new AuthQqRequest(AuthConfig.builder()
                .clientId("appid")
                .clientSecret("appsecret")
                .redirectUri("http://xxx.com/plugin/qqlogin/callback")
                .unionId(true)
                .build());
    }

    /**
     * 跳转至登录页面
     *
     * @param response 页面跳转
     * @throws IOException
     */
    @RequestMapping("/qqlogin")
    public void qqlogin(HttpServletResponse response) throws IOException {
    
    
        //获取实例
        AuthRequest authRequest = getQQAuthRequest();
        //打印生成的链接
        System.out.println("生成登录链接:" + authRequest.authorize("yourState"));
        System.out.println();
        //页面跳转,其中state参数可自定义
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }

    /**
     * QQ登录成功后返回到此页
     *
     * @param callback 登录用户的信息
     * @return
     */
    @RequestMapping("/qqlogin/callback")
    public Object qqloginCallback(AuthCallback callback) {
    
    
        //获取实例
        AuthRequest authRequest = getQQAuthRequest();
        // 打印返回的授权信息(QQ登录为code,通过code获取用户信息)
        System.out.println(callback.getCode());
        //根据返回的参数,执行登录请求(获取用户信息)
        AuthResponse<AuthUser> authResponse = authRequest.login(callback);

        //打印授权返回代码(2000 表示成功,可以用来判断用户登录成功与否)
        System.out.println("状态码:" + authResponse.getCode());

        //打印用户的昵称、ID、头像等基本信息
        System.out.println("用户的UnionID:" + authResponse.getData().getUuid());
        System.out.println("用户的昵称:" + authResponse.getData().getNickname());
        System.out.println("用户的头像:" + authResponse.getData().getAvatar());

        //打印用户的Token中的信息
        System.out.println("access_token:" + authResponse.getData().getToken().getAccessToken());
        System.out.println("用户的OpenId:" + authResponse.getData().getToken().getOpenId());

        // 打印更加详细的信息(第三方平台返回的原始用户信息)
        System.out.println("用户的城市:" + authResponse.getData().getRawUserInfo().getInnerMap().get("city"));
        System.out.println("用户的年份:" + authResponse.getData().getRawUserInfo().getInnerMap().get("year"));

        // 将JSON包返回到前端页面显示
        return authResponse;
    }

    /**
     * 生成微博授权登录的类getWbAuthRequest
     *
     * @return
     */
    private AuthRequest getWbAuthRequest() {
    
    
        return new AuthWeiboRequest(AuthConfig.builder()
                .clientId("appid")
                .clientSecret("appsecret")
                .redirectUri("http://xxx.com/plugin/sinalogin/callback")
                .build());
    }

    /**
     * 微博登录页面,生成登录链接
     *
     * @param response 页面跳转
     * @throws IOException
     */
    @RequestMapping("/wblogin")
    public void wblogin(HttpServletResponse response) throws IOException {
    
    
        //生成对象
        AuthRequest authRequest = getWbAuthRequest();
        //生成链接并返回,其中state可以自定义,可以用来实现第四方登录
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }

    /**
     * 回调页面,获取登录用户新
     *
     * @param callback 详细JSON信息
     * @return
     */
    @RequestMapping("/sinalogin/callback")
    public Object wbloginCallback(AuthCallback callback) {
    
    
        //生成对象
        AuthRequest authRequest = getWbAuthRequest();
        //获取登录后返回的用户信息结果
        AuthResponse<AuthUser> authResponse = authRequest.login(callback);

        //打印授权返回代码(2000 表示成功,可以用来判断用户登录成功与否)
        System.out.println("状态码:" + authResponse.getCode());

        //打印用户的昵称、ID、头像等基本信息
        System.out.println("用户的UnionID:" + authResponse.getData().getUuid());
        System.out.println("用户的昵称:" + authResponse.getData().getNickname());
        System.out.println("用户的头像:" + authResponse.getData().getAvatar());

        //打印用户的Token中的信息
        System.out.println("access_token:" + authResponse.getData().getToken().getAccessToken());
        System.out.println("用户的OpenId:" + authResponse.getData().getToken().getOpenId());

        // 打印更加详细的信息(第三方平台返回的原始用户信息)
        System.out.println("用户的城市:" + authResponse.getData().getRawUserInfo().getInnerMap().get("city"));
        System.out.println("用户的年份:" + authResponse.getData().getRawUserInfo().getInnerMap().get("year"));
        //返回JSON信息
        return authResponse;
    }

    /**
    * 销毁
    */
    @RequestMapping("/revoke/{token}")
    public Object revokeAuth(@PathVariable("token") String token) throws IOException {
    
    
        AuthRequest authRequest = getWbAuthRequest();
        return authRequest.revoke(AuthToken.builder().accessToken(token).build());
    }

}

当然,还有其他各个平台,如:

image-20210324153959885

每个平台的授权登录实现方式大同小异,此处不再赘述。

参考资料:

  1. https://justauth.wiki/quickstart/explain.html
  2. https://justauth.wiki/oauth/qq.html
  3. https://justauth.wiki/oauth/weibo.html

猜你喜欢

转载自blog.csdn.net/cxh_1231/article/details/115080071