JustAuth 是一个开箱即用的整合第三方登录的开源组件,网上没有搜到它在前后端分离系统中的使用案例,本篇文章将以 QQ 登录为例为大家讲解该场景下的使用步骤,建议收藏 

01

申请 QQ 应用

登录 QQ 互联平台

https://connect.qq.com

申请开发者

进入“应用管理”页面:https://connect.qq.com/manage.html#

如果是第一次使用,并且未进行过开发者认证,需要提交一下个人资料,待认证通过后方可创建应用。

添加应用

依次点击:应用管理 -> 网站应用 -> 创建应用,应用信息提交后,等待审核通过即可。

应用审核通过后如下:

此时可以在登录页面上放置 QQ 图标:

02

后台集成 JustAuth

引入依赖

<dependency><groupId>me.zhyd.oauth</groupId><artifactId>JustAuth</artifactId><version>${latest.version}</version>
</dependency>

${latest.version}表示当前最新的版本

配置 QQ 公众平台信息

#你的appid
oauth.qq.client_id=101***893
#你的appkey
oauth.qq.client_secret=e45862****************008c244ec5
#你接收响应code码地址
oauth.qq.redirect_uri=https://account.qiwenshare.com/qqprocessing
#腾讯获取code码地址
oauth.qq.code_callback_uri=https://graph.qq.com/oauth2.0/authorize
#腾讯获取access_token地址
oauth.qq.access_token_callback_uri=https://graph.qq.com/oauth2.0/token
#腾讯获取openid地址
oauth.qq.openid_callback_uri=https://graph.qq.com/oauth2.0/me
#腾讯获取用户信息地址
oauth.qq.user_info_callback_uri=https://graph.qq.com/user/get_user_info

配置类,用来读取配置信息

@Component
@ConfigurationProperties(prefix = "oauth")
@Data
public class OAuthProperties {private QQProperties qq = new QQProperties();//这里可以定义很多其他三方账户配置
}
@Data
public class QQProperties {private String client_id;private String client_secret;private String redirect_uri;private String code_callback_uri;private String access_token_callback_uri;private String openid_callback_uri;private String user_info_callback_uri;}

03

QQ 登录认证操作

当用户点击登录界面点击 QQ 登录按钮之后,调用后台接口,后台接口如下:

@Operation(summary = "第三方登陆对外接口")
@GetMapping(value = "/render/{source}")
public void render(@PathVariable("source") String source, HttpServletResponse response) throws IOException {AuthRequest authRequest = getAuthRequest(source);String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());response.sendRedirect(authorizeUrl);
}

从上面代码可以看出,这里使用了JustAuth 提供的 AuthRequest 方法,source参数用来区分三方账户类型。

这里使用 QQ 登录方式,那么传进来的请求路径实际上是 /render/qq , 接下来是 getAuthRequest 方法 :

private AuthRequest getAuthRequest(String source) {AuthRequest authRequest = null;switch (source.toLowerCase()) {case "qq":authRequest = new AuthQqRequest(AuthConfig.builder().clientId(oauth.getQQ().getClient_id()).clientSecret(oauth.getQQ().getClient_secret()).redirectUri(oauth.getQQ().getRedirect_uri()).build());break;case "wechat_open":authRequest = new AuthWeChatOpenRequest(AuthConfig.builder().clientId("").clientSecret("").redirectUri("http://www.zhyd.me/oauth/callback/wechat").build());break;case "csdn":authRequest = new AuthCsdnRequest(AuthConfig.builder().clientId("").clientSecret("").redirectUri("http://dblog-web.zhyd.me/oauth/callback/csdn").build());break;//这里还有好多case省略default:break;}if (null == authRequest) {throw new AuthException("未获取到有效的Auth配置");}return authRequest;
}

通过上面代码可以看出,定义一个接口即可兼容多种三方账户的登录,上面的AuthQqRequest 需要传入的参数。

可以在 https://connect.qq.com/index.html 页面去创建网址应用并申请得到,其他的三方账号也是同样的操作。

这个接口执行完成之后,会跳转至 QQ 登录认证界面。

认证成功之后,继续跳转至我们的项目中,当跳转到我们的项目之后,请求路径会同时会在地址栏带回登录需要的参数(code,state),如下:

https://www.qiwenshare.com/qqprocess?code=9A5F************************06AF&state=test

04

回调地址的处理

这个回调的地址,可以在 QQ 互联中心进行设置。

但是这里有一个需要注意的点,因为这篇文章题目讲的是前后端分离系统,那么回调地址是设置为前端还是后台,JustAuth 文档和案例都是通过后台来接收这个回调请求的,我们先来看一下它的代码段源码,如下:

/*** oauth平台中配置的授权回调地址,以本项目为例,在创建github授权应用时的回调地址应为:http://127.0.0.1:8443/oauth/callback/github*/
@RequestMapping("/callback/{source}")
public ModelAndView login(@PathVariable("source") String source, AuthCallback callback, HttpServletRequest request) {log.info("进入callback:" + source + " callback params:" + JSONObject.toJSONString(callback));AuthRequest authRequest = getAuthRequest(source);AuthResponse<AuthUser> response = authRequest.login(callback);log.info(JSONObject.toJSONString(response));if (response.ok()) {userService.save(response.getData());return new ModelAndView("redirect:/users");}Map<String, Object> map = new HashMap<>(1);map.put("errorMsg", response.getMsg());return new ModelAndView("error", map);
}

从上面可以看出,后台接收 QQ 回调地址,处理完成后直接返回 ModelAndView 到前端,这里的做法显然不符合前后端分离系统,所以我的做法是用前端代码去接收回调地址,并请求后台:

<template><div class="bind-qq"><i class="link-qq-icon el-icon-link">正在登录,请稍等...</i></div>
</template>
<script>
import { authorize, bind } from '@/request/user.js'
import Cookies from 'js-cookie'export default {name: 'QQProcessing',data() {return {openid: '',access_token: '',waitInfo: ''}},created() {let code = this.$route.query.codelet state = this.$route.query.state},methods: {authorize(code, state) {let data = {code: code,state: state}authorize(data).then(res => {if (res.success) {this.$store.dispatch('getUserInfo').then(() => {this.$router.push({path: '/'})})} else {this.$message.error(res.message)}})}}
}
</script>

上段代码便是从地址里面去拿到 code,state 这两个参数,并请求后台接口。

05

获取第三方用户信息并认证

后台拿到前台传过来的 code 和 state 参数之后,便可以获取第三方用户信息并认证,代码段如下:

@Operation(summary = "第三方平台登录",  tags = {"user"})
@MyLog(operation = "第三方平台登录")
@RequestMapping(value = "/authorize/{source}", method = RequestMethod.POST)
public RestResult<UserLoginVo> authorize(@PathVariable("source") String source, @RequestBody AuthorizeDto authorizeDto) {RestResult<UserLoginVo> restResult = new RestResult<UserLoginVo>();AuthRequest authRequest = getAuthRequest(source);AuthCallback authCallback = new AuthCallback();authCallback.setCode(authorizeDto.getCode());authCallback.setState(authorizeDto.getState());AuthResponse<AuthUser> response = authRequest.login(authCallback);AuthUser authUser = response.getData();String openId = authUser.getUuid();//1、使用openid查询用户SocialUser socialUser = socialUserService.getSocialUserByUUID(openId, source);if (socialUser == null) {  //没有注册过socialUser = socialUserService.createAndInitSocialUser(authUser, source);}socialUser.setAvatar(authUser.getAvatar());socialUser.setUsername(authUser.getUsername());socialUser.setNickname(authUser.getNickname());socialUserService.updateById(socialUser);long userId = socialUserAuthService.getUserIdBySocialUserId(socialUser.getSocialUserId());userSourceMap.put(userId, source);String jwt = getToken(userId);UserBean userLoginTime = new UserBean();userLoginTime.setUserId(userId);userLoginTime.setLastLoginTime(DateUtil.getCurrentTime());userService.updateUserInfo(userLoginTime);UserBean user = userService.getById(userId);UserLoginVo userLoginVo = new UserLoginVo();BeanUtil.copyProperties(user, userLoginVo);userLoginVo.setToken(jwt);restResult.setData(userLoginVo);restResult.setSuccess(true);return restResult;
}

认证完成之后,后台会生成token并完成整个登录过程。

06


附录:数据库表

一共涉及三张表,分别是:

  • 用户表:user

  • 三方用户表: socialuser

  • 关联表:social_user_auth

因为一个用户可以绑定多个三方账号,QQ,微信,微博等等,所以用户表和三方用户表实际上是一对多的关系,表设计如下:

CREATE TABLE `user` (`userId` BIGINT(20) NOT NULL AUTO_INCREMENT,`addrarea` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`addrcity` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`addrprovince` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`birthday` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`industry` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`intro` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`password` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`position` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`salt` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`sex` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`telephone` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`username` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`email` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`imageurl` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`registertime` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`lastlogintime` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`editortype` INT(11) NULL DEFAULT NULL,`userStateId` INT(11) NULL DEFAULT NULL,`available` INT(11) NULL DEFAULT NULL,`modifyTime` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`modifyUserId` BIGINT(20) NULL DEFAULT NULL,`openId` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`realname` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`qqPassword` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`qqStatus` BIGINT(20) NULL DEFAULT NULL COMMENT '1迁移数据,0废弃数据',PRIMARY KEY (`userId`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2229
;
CREATE TABLE `socialuser` (`socialUserId` BIGINT(20) NOT NULL AUTO_INCREMENT,`accessCode` VARCHAR(200) NULL DEFAULT NULL COMMENT '个别平台的授权信息' COLLATE 'utf8mb4_general_ci',`accessToken` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户的授权令牌' COLLATE 'utf8mb4_general_ci',`code` VARCHAR(200) NULL DEFAULT NULL COMMENT '用户的授权code' COLLATE 'utf8mb4_general_ci',`expireIn` INT(11) NULL DEFAULT NULL COMMENT '第三方用户的授权令牌的有效期',`idToken` VARCHAR(200) NULL DEFAULT NULL COMMENT 'id token' COLLATE 'utf8mb4_general_ci',`macAlgorithm` VARCHAR(200) NULL DEFAULT NULL COMMENT '小米平台用户的附带属性' COLLATE 'utf8mb4_general_ci',`macKey` VARCHAR(200) NULL DEFAULT NULL COMMENT '小米平台用户的福袋属性' COLLATE 'utf8mb4_general_ci',`oauthToken` VARCHAR(200) NULL DEFAULT NULL COMMENT 'Twitter平台用户的附带属性' COLLATE 'utf8mb4_general_ci',`oauthTokenSecret` VARCHAR(200) NULL DEFAULT NULL COMMENT 'Twitter平台用户的附带属性' COLLATE 'utf8mb4_general_ci',`openId` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户的openId' COLLATE 'utf8mb4_general_ci',`refreshToken` VARCHAR(200) NULL DEFAULT NULL COMMENT '刷新令牌' COLLATE 'utf8mb4_general_ci',`scope` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户授予的权限' COLLATE 'utf8mb4_general_ci',`source` VARCHAR(20) NULL DEFAULT NULL COMMENT '第三方用户来源 GITHUB,QQ,GITEE,具体参考 AuthDefaultSource' COLLATE 'utf8mb4_general_ci',`tokenType` VARCHAR(200) NULL DEFAULT NULL COMMENT '个别平台的授权信息' COLLATE 'utf8mb4_general_ci',`uId` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户的ID' COLLATE 'utf8mb4_general_ci',`unionId` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户的unionid' COLLATE 'utf8mb4_general_ci',`uuid` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方系统的唯一ID' COLLATE 'utf8mb4_general_ci',`nickname` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户的nickname' COLLATE 'utf8mb4_general_ci',`username` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户的username' COLLATE 'utf8mb4_general_ci',`avatar` VARCHAR(200) NULL DEFAULT NULL COMMENT '第三方用户的头像' COLLATE 'utf8mb4_general_ci',PRIMARY KEY (`socialUserId`) USING BTREE,UNIQUE INDEX `UK_62b04yxuaqg0qfflxxfgjrd42` (`uuid`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=415
;
CREATE TABLE `social_user_auth` (`socialUserAuthId` BIGINT(20) NOT NULL AUTO_INCREMENT,`socialUserId` BIGINT(20) NULL DEFAULT NULL,`userId` BIGINT(20) NULL DEFAULT NULL,PRIMARY KEY (`socialUserAuthId`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=424
;

07


号外!号外!

本文作者在蓝桥云课上线《经典项目:前后端分离网盘系统实战》课程啦!本课程主要使用 Spring Boot 2 和 Vue CLI@4 来开发 Web 端网盘系统。

如果你想开发一个网盘系统,此门课程将手把手教你,通过实战的方式,提高你的技术水平。

以下为课程知识重点:

如果你想学习更多此本课程,欢迎通过文末方式领取八折优惠哦~

在 Spring Boot 前后端分离系统中集成 JustAuth 实现第三方账号登录?相关推荐

  1. sm4 前后端 加密_这7个开源的Spring Boot前后端分离项目整理给你

    来源|公众号:江南一点雨 前后端分离已经开始逐渐走进各公司的技术栈,不少公司都已经切换到前后端分离开发技术栈上面了,因此建议技术人学习前后端分离开发以提升自身优势.同时,也整理了 7 个开源的 Spr ...

  2. Vue+Spring boot前后端响应流程总结

    Vue+Spring boot前后端响应流程总结 前端请求页面路径,首先会经过路由: 经过解决跨域问题以后,就会请求到后端接口,后端接口返回的数据会封装到then回调方法的res参数中. 经过回调函数 ...

  3. 《Vue+Spring Boot前后端分离开发实战》专著累计发行上万册

                杰哥的学术专著<Vue+Spring Boot前后端分离开发实战>由清华大学出版社于2021年3月首次出版发行,虽受疫情影响但热度不减,受到业界读者的热捧,截至今日 ...

  4. spring boot 前后端分离项目(商城项目)学习笔记

    spring boot 前后端分离项目(商城项目)学习笔记 目录 spring boot 前后端分离项目(商城项目)学习笔记 后端配置 springboot项目 pom.xml文件 maven 配置文 ...

  5. Spring Boot前后端分离项目Session问题解决

    Spring Boot前后端分离项目Session问题解决 参考文章: (1)Spring Boot前后端分离项目Session问题解决 (2)https://www.cnblogs.com/sooo ...

  6. Spring Boot前后端分离之后端开发

    Spring Boot前后端分离开发之后端开发 前后端分离开发概述 相关术语 前后端分离开发概述 接口规范 RESTful API的理解 RESTful风格的特点 URI规范 路径 请求方式 过滤条件 ...

  7. Spring security 集成 JustAuth 实现第三方授权登录

    Spring security 集成 JustAuth 实现第三方授权登录脚手架: 一.特性 spring security 集成 JustAuth 实现第三方授权登录 : 此项目从 用户管理脚手架( ...

  8. (二十二)admin-boot项目之集成just-auth实现第三方授权登录

    (二十二)集成just-auth实现第三方授权登录 项目地址:https://gitee.com/springzb/admin-boot 如果觉得不错,给个 star 简介: 这是一个基础的企业级基础 ...

  9. 前后端分离项目_七个开源的 Spring Boot 前后端分离项目,一定要收藏

    来自公众号:江南一点雨 前后端分离已经在慢慢走进各公司的技术栈,根据松哥了解到的消息,不少公司都已经切换到这个技术栈上面了.即使贵司目前没有切换到这个技术栈上面,松哥也非常建议大家学习一下前后端分离开 ...

最新文章

  1. 由浅入深剖析.htaccess
  2. C语言中结构体的位域(bit-fields)
  3. 大熊君说说JS与设计模式之(门面模式Facade)迪米特法则的救赎篇------(监狱的故事)...
  4. 微信小程序 body属性的问题
  5. Python面试题大全(一):基础知识学习
  6. OkHttp透明压缩,收获性能10倍,外加故障一枚
  7. python 密码学计算_python 密码学示例——理解哈希(Hash)算法
  8. Kafka Streams简介: 让流处理变得更简单
  9. 工具-Sublime Text:便捷设置 小三角
  10. Service通信详解
  11. Web 爬虫 Apache Nutch 1.15 发布,支持 Java 10
  12. mysql5.7系列使用记录信息
  13. HTML表格表单案例
  14. 【Python代码实践】使用Python批量获取雨课堂图片
  15. windows中 FFmpeg 配置libx264 遇到的坑和解决办法
  16. SDUT-2933-人活着系列之Streetlights (Kruskal)
  17. App Store Connect显示app已经上架(可供销售),但在App Store中没有实时更新
  18. Docker容器搭建conpot蜜罐
  19. 【操作系统】操作系统知识点整理;C++ 实现线程池与windows 线程池的使用;
  20. 英语单词记忆的词根总结

热门文章

  1. 京东小程序折叠屏适配探索 | 京东云技术团队
  2. 有商业构想没经验?这些创始人证明即使这样也能成功
  3. Android横幅通知栏自定义
  4. 搭建kafka消息队列服务
  5. oracle监听的日志,Oracle监听日志定期清理
  6. 用机器学习sklearn+opencv-python过古诗文网4位数字+字母混合验证码
  7. 01Python基础阶段_Day07
  8. Flutter布局锦囊---有背景图的头像选择
  9. 如何开高效会议?教你9招
  10. 一杯敬明天,一杯敬过往--我的半年总结