正文 Spring Boot开发之使用JustAuth组件实现第三方登录(QQ、微博等) 拾年之璐 V管理员 /2021年 /1948 阅读 0401 [TOC] > 在我们的项目开发中,使用`第三方登录`(如QQ登录、微信登录等)可以更加方便、轻松地实现用户登录。 > > 在以往的开发过程中,如果要使项目实现第三方登录功能,一般过程是`阅读官网的开发文档`,并`下载其JDK`(或者依赖pom),然后进行开发实现。 > > 但是,如果网站要实现`多个`第三方平台的登录功能,则需要很高的`学习成本`。 > > 所以,就有开发者实现了一款基于Spring Boot的**开箱即用的整合第三方登录的开源组件:JustAuth** > > 该插件的网址:[https://justauth.wiki](https://justauth.wiki) > > 本文将基于`Spring Boot`,使用`JustAuth组件`实现第三方快捷登录,并获取用户的uid。 ### 1. 插件简介 首先给出几个链接: + 组件的帮助文档:[https://justauth.wiki](https://justauth.wiki) + 组件的GitHub:[https://github.com/justauth/JustAuth](https://github.com/justauth/JustAuth) + 组件的Gitee组织:[https://gitee.com/justauth/](https://gitee.com/justauth/) 在组件的各个网址,都可以看到关于该组件的自述: > 小而全而美的第三方登录开源组件。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐、Gitlab、美团、饿了么和推特等第三方平台的授权登录。 Login, so easy! 可以看出,此组件支持的第三方登录平台,是非常全的。 下面,以`QQ`和`微博`为例,测试`Spring Boot`上使用该组件完成第三方登录快速开发。 > + 建议参考组件的帮助文档,均有详细说明。 > + 或者直接参考官方的demo:https://github.com/justauth/JustAuth-demo > + 开发之前,需要前往`QQ开放平台`和`微博开放平台`申请应用,并获取 `appid`、`APP secret` ,设置`callback url`。参考: > + [https://blog.csdn.net/cxh_1231/article/details/114984731](https://blog.csdn.net/cxh_1231/article/details/114984731) > + [https://blog.csdn.net/cxh_1231/article/details/115068413](https://blog.csdn.net/cxh_1231/article/details/115068413) ### 2. 项目创建与导包 关于Spring Boot项目如何创建,可参考以下几篇文章: > + [Idea创建Spring Boot项目](https://cxhit.blog.csdn.net/article/details/113782979) > + [Spring Boot 常用配置](https://cxhit.blog.csdn.net/article/details/113794408) > + [Spring Boot整合Thymeleaf](https://cxhit.blog.csdn.net/article/details/113794422) > > 只看前两个就可以了。 创建完成项目之后,在`pom.xml`文件中,导入依赖: ```xml me.zhyd.oauth JustAuth 1.15.9 cn.hutool hutool-http 5.3.9 org.projectlombok lombok 1.18.12 provided com.alibaba fastjson 1.2.62 ``` 这里导入了4个依赖,说明如下: + 第一个是`JustAuth组件`必须的,版本就默认使用帮助文档中的最新版,如下图所示。  + 第二个是`http请求`的实现组件,这是因为`JustAuth从 v1.14.0 `开始不会默认集成`hutool-http`,需要单独添加; + 第三个是`lombok`,即自动生成模型的`set`和`get`。因为项目中授权登录成功后,返回的`用户信息模型`,使用了`@Getter`和`@Setter`注解。添加此组件,方便获取用户的单个信息(如uuid、nickname等)。  + 第四个是阿里巴巴的`fastjson` 组件,因为在返回的结果中,有些信息是封装成 `JSON格式` 的,需要使用该组件来转换成 `map键值对` ,从而获取对我们有用的信息。  ### 3. QQ登录实现 **1、 新建一个名为 `PluginController` 的控制类,用来实现第三方插件的登录回调功能。** ```java 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`接口的实现类,用于`生成登录链接`、`登录并获取用户信息`等** ```java /** * 授权接口类 * * @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、生成`登录链接`并`跳转至登录页面`** ```java /** * 跳转至登录页面 * * @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  浏览器访问`http://XXX.com/plugin/qqlogin`,即可跳转至此登录页面  **4、回调页面`plugin/qqlogin/callback`获取返回的`code`,并通过返回的`code`获取当前登录用户的`信息`** 如下: ```java /** * QQ登录成功后返回到此页 * * @param callback 登录用户的信息 * @return */ @RequestMapping("/qqlogin/callback") public Object qqloginCallback(AuthCallback callback) { AuthRequest authRequest = getQQAuthRequest(); // 打印返回的授权信息(QQ登录为code,通过code获取用户信息) System.out.println(callback.getCode()); //根据返回的参数,执行登录请求(获取用户信息) AuthResponse authResponse = authRequest.login(callback); // 将JSON包返回到前端页面显示 return authResponse; } ``` 上面的图,点击登录后,即可跳转至回调页面,显示出所有的JSON数据,如下图所示:  我们对该`JSON数据`进行分析: + 返回的`code`是2000,表示获取数据成功(其他代码,可以在https://justauth.wiki/quickstart/error_code.html查看); + 返回的`data`数据,包含所有有用的信息,如用户ID`uuid`、用户昵称`username`、头像`avatar`、性别`gender`等等; + `data`下的`toke`n,包含所有的`获取token`、`刷新token`和`过期时间`等信息; + `data下`的`rawUserInf`o,是第三方平台返回的`原始用户信息`; + 关于返回的JSON数据的每个子段的`详细含义`,都可以在`模型AuthUser`中查看。 **5、后台获取指定信息** 上面可以获取所有的用户信息,但这些信息,我们并不能这样使用,而是筛选有用的信息,然后保存到自己的数据库。 所以下面这个完整示例,演示如何获取上述JSON中的指定信息。 ```java /** * QQ登录成功后返回到此页 * * @param callback 登录用户的信息 * @return */ @RequestMapping("/qqlogin/callback") public Object qqloginCallback(AuthCallback callback) { //获取实例 AuthRequest authRequest = getQQAuthRequest(); // 打印返回的授权信息(QQ登录为code,通过code获取用户信息) System.out.println(callback.getCode()); //根据返回的参数,执行登录请求(获取用户信息) AuthResponse 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数据,是一一对应的,如下图所示:  打印结果:  ### 4. 微博登录实现 有了上面QQ登录的详细分析,微博登录也是类似的,这里速战速决。 **1、创建授权请求类** ```java /** * 生成微博授权登录的类getWbAuthRequest * * @return */ private AuthRequest getWbAuthRequest() { return new AuthWeiboRequest(AuthConfig.builder() .clientId("appid") .clientSecret("app secret") .redirectUri("http://xxx.com/plugin/sinalogin/callback") .build()); } ``` **2、生成并跳转登录链接** ```java /** * 微博登录页面,生成登录链接 * * @param response 页面跳转 * @throws IOException */ @RequestMapping("/wblogin") public void wblogin(HttpServletResponse response) throws IOException { //生成对象 AuthRequest authRequest = getWbAuthRequest(); //生成链接并返回,其中state可以自定义,可以用来实现第四方登录 response.sendRedirect(authRequest.authorize(AuthStateUtils.createState())); } ``` 结果:  **3、回调页面获取信息,以及返回JSON数据** ```java /** * 回调页面,获取登录用户新 * * @param callback 详细JSON信息 * @return */ @RequestMapping("/sinalogin/callback") public Object wbloginCallback(AuthCallback callback) { //生成对象 AuthRequest authRequest = getWbAuthRequest(); //获取登录后返回的用户信息结果 AuthResponse 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; } ``` 结果:  ### 5. 完整Controller源码 ```java 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 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 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()); } } ``` 当然,还有其他各个平台,如:  每个平台的授权登录实现方式大同小异,此处不再赘述。 > 参考资料: > > 1. https://justauth.wiki/quickstart/explain.html > 2. https://justauth.wiki/oauth/qq.html > 3. https://justauth.wiki/oauth/weibo.html 本文采用创作共用版权 CC BY-NC-SA 3.0 CN 许可协议,转载或复制请注明出处! -- 展开阅读全文 --