SpringSocial简介


这张流程图就是Auth的基本流程,详细文章OAuth协议简介,
这里QQ扫码登录会在整个Auth流程后面加入根据用户信息构建Authentication并放入SecurityContext中,

SpringSocial实际上就是帮我们封装了0-7完整的流程,SpringSocial将上面的流程封装成了SocialAuthenticationFilter,并加入到了SpringSecurity的过滤器链上面,
当我们访问指定请求时,SocialAuthenticationFilter就会将我们这个请求拦截下来,帮我们把0-7整个Auth流程走完,实际也就完成了QQ扫码授权登录,这就是SpringSocial所做的事情,上面的那张流程图实际上是逻辑上的流程图,
下面开始介绍SpringSocial实际上是如何帮我们处理Auth流程的!

在0-6这些步骤实际上是和服务提供商建立关系的,


ServiceProvider: 实际上就是服务提供商的抽象,针对每一个服务提供商,如QQ、微信、微博都需要特有的接口实现,AbstractOAuth2ServiceProvider这个抽象类就帮我们实现了一些公有的东西,我们在实现QQ、微信、微博授权登录的时候只需要实现AbstractOAuth2ServiceProvider这个抽象类就可以了,这里需要注意OAuth2,这里使用的都是OAuth2协议,在这之前还有OAuth1协议的,在国外一些网站如TW使用的就是OAuth1协议的,国外用的比较早,所以用的1的多,国内用的比较晚,所以用的2就比较多了
OAuth2Operations: 这个接口是封装OAuth相关操作 ,实际上这个接口封装了1-5的标准OAuth协议流程,SpringSocial还提供了OAuth2Template默认实现,这类会帮我们完成OAuth1-5执行的流程
API: 实际上没有明确的接口,因为每一个服务提供商对获取用户信息都是有区别的,这个接口就是我们要获取用户信息的接口,同时这里SpringSocial也给我们获取不同服务提供商的用户数据抽象了一个AbstractOAuth2ApiBinding类,来帮助我们更加快速规范的的获取不同服务提供商获取用户信息的实现

在SpringSocial的基本流程中的第7步就和服务提供商没有关系了,这一步都是在我们应用内部完成的,下面就来看看和第七不相关的类!

Connection: 这个类的作用就是用来封装我没在第6步时从服务提供商那获取到的用户信息,我们代码中用到的实现类就是OAuth2Connection。

Connection实际上就是由ConnectionFactory创建出来的,实际创建的类就是OAuth2ConnectionFactory,这里为了创建OAuth2Connection这个对象就需要走1-6这些流程获取用户信息,那么实际上图中右边的这部份又是封装在ConnectionFactory这里面的,那么实际上在ConnectionFactory里面有ServiceProvider帮我们实现1-6的流程,获取到用户信息,然后将用户信息封装成Connection对象,Connection实际上是一个固定的结构,它的字段,字段名都是固定的,在上面有提到每个服务商获取用户信息都是不一样的,那么这里就是ApiAdapter来处理的事情,ApiAdapter会将不同服务提供商获取到的不同用户数据做一个适配,那么在回到Connection中来,Connection是来封装服务提供商返回的用户信息,在我们的业务系统里面,一般我们会将用户信息存在一张user的表里面,那么我们业务系统中的用户信息如何和服务提供商的信息关联到一块呢,换句话说就是我服务提供商中的A用户扫码授权登录了,那我们系统怎么知道是那个用户登录了,那么实际上这种用户信息对应关系实际上是存储在数据库中的,在数据库中会创建一张UserConnection表这张表中会有一个userId会和我们业务系统中的userId相对应,那么实际查询数据库的操作就是交给了UserConnectonRepository,实际在代码中使用的就是JdbcUserConnectionRepository这个类,这个类的实际作用就是去操作上面提到的UserConnection这张表,那么这就是SpringSocial的基本概念

QQ授权登录

在上篇文章中将SpringSocial的基本概念时提到服务提供商的用户数据是封装在Connection中的,现在要解决的第一个问题就是要拿到Connection,那么得到一个Connection那么就要得到一个ConnectionFactory这样一个工厂,那么我们在构建ConnectionFactory这个工厂的时候就需要ServiceProvider和ApiAdapter那么在ServiceProvider中又需要两个东西,一个是OAuth2Operations和Api(获取用户信息的)和服务提供商密歇相关的,那么我们编码的流程就是先完成Api获取用户信息的实现,然后有了Api,在使用的OAuth2Template作为OAuth2Operations的默认实现,那么有了Api和OAuth2Operations就可以构建出ServiceProvider,有了ServiceProvider之后在去完成ApiAdapter,那么ServiceProvider和ApiAdapter有了那么就可以完成ConnectionFactory的工厂构建,那么有了ConnectionFactory这个工厂我们就可以的到Connection用户信息,那么有了用户信息后我们就会在数据库中建一张表,UserConnection,然后对这张表的操作SpringSocial也为我们提供好了UsersConnectionRepository,我们只需要简单配置一下,告诉他数据库在哪就可以了,那么我们在Connection用户信息封装类中得到用户信息,和从我们UserConnection表中得到服务提供商用户数据和我们业务系统关联的用户数据,那么我们完整的QQ扫码授权流程就可以跑起来了!

1.编写获取用户信息API

/**
* @description: 获取QQ用户信息
* @author TAO
* @date 2020/10/13 10:44
*/
public interface QQ {//获取QQ用户信息QQUserInfo getUserInfo();
}
package com.security.socia.qq.api;import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.oauth2.TokenStrategy;import java.io.IOException;/**
* @description: 获取QQ用户信息的实现类,每个获取服务商数据的都需要继承AbstractOAuth2ApiBinding类
* @author TAO
* @date 2020/10/13 10:46
*/
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ{//jsonReq.put("oauth_consumer_key","1110659437");private static final  String URL_GET_OPENID="https://graph.qq.com/oauth2.0/me?access_token=%s";//获取openIdprivate static final String URL_GET_USERINFO="https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";//获取用户信息;private String appId;//APPIdprivate String openId;//授权用户Idprivate ObjectMapper objectMapper=new ObjectMapper();public QQImpl(String accessToken,String appId){/*** 默认是将accessToken作为请求头传递*     protected AbstractOAuth2ApiBinding(String accessToken) {*         this(accessToken, TokenStrategy.AUTHORIZATION_HEADER);*     }*/super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);//这种类型是将accessToken作为参数传递this.appId=appId;String url=String.format(URL_GET_OPENID,accessToken);String result=getRestTemplate().getForObject(url,String.class);System.out.println(result);this.openId= StringUtils.substringBetween(result,"\"openid\":","}");}/*** 获取用户信息* @return* @throws IOException*/@Overridepublic QQUserInfo getUserInfo(){String url=String.format(URL_GET_USERINFO,appId,openId);String result=getRestTemplate().getForObject(url,String.class);System.out.println(result);try {return objectMapper.readValue(result,QQUserInfo.class);//将返回回来的字符串转换成QQUserInfo对象} catch (IOException e) {e.printStackTrace();throw new RuntimeException("获取用户信息失败");}}
}
/**
* @description: QQ用户信息封装类
* @author TAO
* @date 2020/10/13 10:43
*/
public class QQUserInfo {private String ret;//返回码private String msg;//如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。private String openId;//文档没写,但实际api中返回里存在,这是用户的唯一标识private String nickname;//用户在QQ的昵称。private String figureurl;//大小为30×30像素的QQ空间头像URL。private String figureurl_1;//    大小为50×50像素的QQ空间头像URL。private String figureurl_2;//大小为100×100像素的QQ空间头像URL。private String figureurl_qq_1;//大小为40×40像素的QQ头像URL。private String figureurl_qq_2;//大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。private String gender;//性别。 如果获取不到则默认返回"男"private String is_yellow_vip;private String vip;private String yellow_vip_level;private String level;private String is_yellow_year_vip;public String getRet() {return ret;}public void setRet(String ret) {this.ret = ret;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public String getFigureurl() {return figureurl;}public void setFigureurl(String figureurl) {this.figureurl = figureurl;}public String getFigureurl_1() {return figureurl_1;}public void setFigureurl_1(String figureurl_1) {this.figureurl_1 = figureurl_1;}public String getFigureurl_2() {return figureurl_2;}public void setFigureurl_2(String figureurl_2) {this.figureurl_2 = figureurl_2;}public String getFigureurl_qq_1() {return figureurl_qq_1;}public void setFigureurl_qq_1(String figureurl_qq_1) {this.figureurl_qq_1 = figureurl_qq_1;}public String getFigureurl_qq_2() {return figureurl_qq_2;}public void setFigureurl_qq_2(String figureurl_qq_2) {this.figureurl_qq_2 = figureurl_qq_2;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getIs_yellow_vip() {return is_yellow_vip;}public void setIs_yellow_vip(String is_yellow_vip) {this.is_yellow_vip = is_yellow_vip;}public String getVip() {return vip;}public void setVip(String vip) {this.vip = vip;}public String getYellow_vip_level() {return yellow_vip_level;}public void setYellow_vip_level(String yellow_vip_level) {this.yellow_vip_level = yellow_vip_level;}public String getLevel() {return level;}public void setLevel(String level) {this.level = level;}public String getIs_yellow_year_vip() {return is_yellow_year_vip;}public void setIs_yellow_year_vip(String is_yellow_year_vip) {this.is_yellow_year_vip = is_yellow_year_vip;}public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}
}

这里使用SpringSocial默认提供的OAuth2Template来实现,所以这里就直接跳过这个流程
2.完成ServiceProvider

/**
* @description: 负责处理OAuth整个流程的
* @author TAO
* @date 2020/10/14 20:56
*/
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {private String appId;private static final String URL_AUTHRIZE="https://graph.qq.com/oauth2.0/authorize";//获取授权码//https://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_tokenprivate static final String URL_ACCESS_TOKEN="https://graph.qq.com/oauth2.0/token";//通过授权码获取tokenpublic QQServiceProvider(String appId,String appSecret){//OAuth2Template 使用SpringSocial默认流程实现super(new OAuth2Template(appId,appSecret,URL_AUTHRIZE,URL_ACCESS_TOKEN));}@Overridepublic QQ getApi(String accrssToken) {return new QQImpl(accrssToken,appId);}
}

那么编码到这里我们流程图中左边的ServiceProvider部分就编写完成了

3.编写用户信息适配类

/**
* @description: QQ用户信息适配类
* @author TAO
* @date 2020/10/19 21:27
*/
public class QQAdapter implements ApiAdapter<QQ>{@Overridepublic boolean test(QQ qq) {//测试QQ的服务是否是通的return true;}//将服务提供商获取的用户数据存放到标准的处理对象中@Overridepublic void setConnectionValues(QQ qq, ConnectionValues connectionValues) {System.out.println("setConnectionValues");QQUserInfo qqUserInfo=qq.getUserInfo();connectionValues.setDisplayName(qqUserInfo.getNickname());//昵称connectionValues.setImageUrl(qqUserInfo.getFigureurl_qq_1());//头像connectionValues.setProfileUrl(null);//个人主页,QQ没有个人主页,这里方空,如果是微博,那么这里就是微博的主页地址connectionValues.setProviderUserId(qqUserInfo.getOpenId());//}//通过api得到用户信息--绑定解绑的时候会用到@Overridepublic UserProfile fetchUserProfile(QQ qq) {return null;}//用作某些更新操作--QQ没有@Overridepublic void updateStatus(QQ qq, String s) {}
}

4.完成QQConnectionFactory的组装

/**
* @description: QQ服务提供商连接工厂
* @author TAO
* @date 2020/10/14 21:17
*/
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {//这里直接将之前写好的QQAdapter和QQServiceProvider创建就可以了public QQConnectionFactory(String providerId, String appId,String appSecret) {super(providerId, new QQServiceProvider(appId,appSecret), new QQAdapter());//providerId 服务提供商的唯一标识}
}

这里的Connection并不需要我们来创建,ConnectionFactory组件完成后会自动帮我们创建出Connection

5.创建服务提供商用户数据和我们业务系统用户数据关联的表
这里表可以点击JdbcUsersConnectionRepository在同目录下会有表,

6.完成UsersConnectionRepository相关编码


/**
* @description: 社交用户数据关联配置
* @author TAO
* @date 2020/10/14 21:21
*/
@Configuration
@EnableSocial
public class SocaialConfig extends SocialConfigurerAdapter {@Autowiredprivate DataSource dataSource;@Autowiredprivate SecurityProperties securityProperties;@Overridepublic UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {JdbcUsersConnectionRepository repository=new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());repository.setTablePrefix("pre_");//这里的表明是无法改变的,只能添加前缀// 这里表可以点击JdbcUsersConnectionRepository在同目录下会有表,如果这里没有前缀要求这一步直接省略,直接返回new ....即可return repository;}/*** CREATE TABLE UserConnection (userId VARCHAR(255) NOT NULL,               自己业务系统的用户id*   providerId VARCHAR(255) NOT NULL,                                       服务提供商的id*   providerUserId VARCHAR(255),                                            openId*     rank INT NOT NULL,                                                      用户等级*   displayName VARCHAR(255),*  profileUrl VARCHAR(512),*   imageUrl VARCHAR(512),*     accessToken VARCHAR(512) NOT NULL,*     secret VARCHAR(512),*   refreshToken VARCHAR(512),*     expireTime BIGINT,*     PRIMARY KEY (userId, providerId, providerUserId));* CREATE UNIQUE INDEX UserConnectionRank ON UserConnection(userId, providerId, rank);*** providerId自己业务系统的用户id、服务提供商的id-providerId、providerUserId-openId这三个字段代表了某个用户在某一个服务提供商上他的openid是什么*/@Beanpublic SpringSocialConfigurer springSocialConfigurer(){return new SpringSocialConfigurer();}
}

那么到这里基本上SpringSocial后端的流流程就基本完毕了,那么前端需要提供一个QQ登录的入口
7.前端提供QQ登录入口

<h3>社交登录</h3><a href="/qqLogin/qq">QQ登录</a>这里社交登录的请求会被SocialAuthenticationFilter拦截

这里的请求是可以自定义配置的

8.自定义配置QQ授权登录拦截请求
这里看到第6步的return new SpringSocialConfigurer();,这里就是默认配置了

在这里加过滤器之前调用了一个方法this.postProcess(filter)

也就是在加入过滤器之前会执行以下这个方法,那么我们就需要自己编写一个类继承SpringSocialConfigurer并重写postProcess这个方法就可以完成自定义QQ授权登录

/**
* @description: 自定义QQ授权登录相关配置
* @author TAO
* @date 2020/10/14 23:40
*/
public class CutomSocaialConfig extends SpringSocialConfigurer {private String filterProcessesUrl;public CutomSocaialConfig(String filterProcessesUrl){this.filterProcessesUrl=filterProcessesUrl;}//修改默认Socaial社交登录的一些配置@Overrideprotected <T> T postProcess(T object) {SocialAuthenticationFilter filter=(SocialAuthenticationFilter)super.postProcess(object);filter.setFilterProcessesUrl(filterProcessesUrl);//修改QQ授权登录的拦截请求return (T) filter;}
}

那么上面第6不的操作就不能使用默认配置了,修改代码如下

  @Beanpublic SpringSocialConfigurer springSocialConfigurer(){CutomSocaialConfig cutomSocaialConfig=new CutomSocaialConfig(securityProperties.getSocial().getFilterProcessesUrl());//将我们自定义的社交登录配置加入//return new SpringSocialConfigurer();使用默认配置return cutomSocaialConfig;}securityProperties.getSocial().getFilterProcessesUrl()这里的数据是自定义在properties文件中

这里注意给QQ互联那边的回调地址一定是不需要认证的接口,否则QQ那边会回调失败的

下面这张图就是SpringSocial执行第三方登录的时候涉及到的一些主要接口和实现类,以及调用顺序,那么实际上适合我们SpringSecurity账号密码登录的流程核心上是一致的

这里也就是QQ授权请求被SocialAuthentication拦截掉,然后经过SocialAuthenticationService,构建出ConnectionFactory工厂,然后封装用户数据Connection,然后又回到SpringSecurity的主流程上到达AuthenticationManager中,这里AuthenticationManager会根据传入的Authentication类型选择AuthenticationManager所管理的AuthenticationProvider来做对应的实现,完成对Connection的处理,在处理的过程中会调用UsersConnectionRepository对服务提供商的用户和我们自己的业务系统用户进行校验,那么校验的正在的校验流程是放在SocialUserDetailsService的接口实现也就是SocialUserDetaild中的,然后通过一系列的检查校验,如果通过了会将用户信息存放在Authentication中然后将这个Authentication标记成以认证的,然后将这个已认证的Authentication存放在SecurityContext中

流程图中蓝色部分的代码我们是不需要动的,橘色部分的代码时由我们自己编写,蓝色部分代码执行是会调用橘色我们自己写的代码

spring social 1.6之后官方不在维护该项目, spring boot 2.x之后也不在提供spring social的 Autoconfiguration
遗弃的原因

SpringSocialConfigurer
OAuth2AuthenticationService
SocialAuthenticationFilter

SpringSocial整合QQ授权登录相关推荐

  1. qq互联登录授权php配置,php如何整合qq互联登录

    相关推荐:<PHP培训> php整合qq互联登录 接入QQ互联平台后,我们就可以让用户通过QQ帐号登录来登陆我们的网站,这样减少了注册的繁琐,可以更快 .更便捷的为了我带来更多的用户,下面 ...

  2. php中qq授权登录,ThinkPHP利用QQ互联实现网站第三方登录(QQ登录)

    接入QQ互联平台后,我们就可以让用户通过QQ帐号登录来登陆我们的网站,这样减少了注册的繁琐,可以更快 .更便捷的为了我带来更多的用户,下面我们一一起来看下如何通过QQ互联来实现第三方登录. ####申 ...

  3. android 9 qq登录,【报Bug】安卓离线打包targetSdkVersion设置28时,在安卓9.0手机上QQ授权登录闪退...

    详细问题描述 [内容] 安卓离线打包,targetSdkVersion设置为28,在安卓9.0手机上运行,下面这段QQ授权登录代码会导致应用闪退或应用重启.低于9.0系统的手机不会出现这个问题.如果t ...

  4. qq授权登录【网站应用】-java版本

    第一步:先去qq互联进行创建网站应用:QQ互联 如下图: 第二步:引入qq的jar包,这里采用maven方式引用 <!--QQ坐标--><dependency><grou ...

  5. qq授权登录实现步骤

    第一步,下载qq授权登录的sdk 第二步:在清单文件中添加腾讯官方的activity ,具体代码在下方 Activity实现: public class MainActivity extends Ac ...

  6. WEB接入第三QQ授权登录

    /**  * 项目: b2b-pc  * 文件: QQAuthority.java  * 包名: com.b2b.pc.auth  * 日期: 2017年11月24日下午8:39:03  * Copy ...

  7. Java实现QQ授权登录网站

    现在的很多网站 为了让用户可以快速登录 使用第三方QQ.微信.微博之类的授权登录     QQ互联的官网地址 1:先去QQ互联申请成为开发者 当审核通过之后  就可以创建一个应用 然后开发实际的登录效 ...

  8. 使用友盟快速集成QQ分享与QQ授权登录

    友盟官方文档 我采用的是手动集成友盟的官方文档.下载SDK进行集成. 解压缩,选择自己需要的文件进行加入到Android Studio中. 由于这里我只需要QQ,那就把QQ文件夹中的所有文件拷贝一份到 ...

  9. php中qq授权登录,php实现qq授权登录

    第一步: 登录 https://connect.qq.com  qq授权管理中心开通应用,审核通过之后填写平台信息.如图: 值得一提的是网站回调域必须具体到方法,不然登录之后回调会识别错误. 第二步: ...

  10. 升级到IOS9以后,QQ授权登录和QQ分享出现问题,不能正常使用了

    手机升级到IOS9以后,QQ授权登录和QQ分享不能用了,不用着急,问题容易解决. 一.QQ登录问题解决 1.我们先将iphone真机(已经升级到ios9)连到电脑上,并通过xcode安装你的程序,然后 ...

最新文章

  1. java类加载的表现形式
  2. CF232C Doe Graphs
  3. 学用MVC4做网站四:公共模型
  4. spring-boot-maven-plugin not found的解决方案
  5. 专为Android加载图片Fresco:详细图解SimpleDraweeView加载图片基础
  6. ABB机器人之LOADDATA
  7. 【渝粤教育】国家开放大学2018年春季 0675-22T中级财务会计(2) 参考试题
  8. 尝试Office 2003 VSTO的开发、部署
  9. Firefox无法启动,提示Profile is yet in use by another Firefox
  10. MySQL保存或更新 saveOrUpdate
  11. NET 下数据库图片的存入与读取
  12. 前端笔记-freemarker模板获取后端数据及提交数据
  13. OpenShift 4 之 GitOps(7)用ArgoCD部署Pacman应用集群
  14. 【工具相关】Web-Sublime Text2的用法(一)
  15. MySQL(8)-----truncate清空表和字段自增
  16. 扬帆际海:shopee跨境电商客服回复流程
  17. [BZOJ5064] [HDU3652] B-number 数位DP
  18. [CUDA报错] CUDA error: device-side assert triggered
  19. 【python初级】 关于time.sleep睡眠时间
  20. 基于uFUN开发板和扩展板的联网校准时钟

热门文章

  1. 外贸网站服务器搬迁方案,WordPress网站迁移到新服务器教程
  2. 基于推特数据挖掘交通事件的城市交通流深度学习预测模型
  3. Tenserflow 情感分类
  4. three.js黑洞穿越动画js特效
  5. c语言智能车跑道检测程序,基于金属检测的智能循迹小车设计
  6. windows11错误代码0x0000011b怎么解决? 0x0000011b问题的相应解决办法
  7. Python代码加密 - 4种方案
  8. 计算机配色原理,电脑配色原理介绍-华强电子网
  9. 阿里巴巴大数据竞赛-天池
  10. 网站安全工程师与渗透测试工程师有哪些区别