这一节,我们要研究SpringBoot2+SpringSecuritySocial来实现第三方登录-QQ登录
开发QQ登录思路
我们先看上一章的图
我们最后需要Connection
,即QQ登录后的用户信息,想要Connection
,就需要ConnectionFactory
,想要ConnectionFactory
,就需要我们自己实现APIAdapter
和ServiceProvider
,对于ServiceProvider
来说,我们需要自己实现Api
,而对于OAuth2Operations
可以使用它默认实现的OAuth2Template
。
所以,我们的开发流程为:
- 自己实现
Api
- 使用默认的
OAuth2Operations
,从而实现ServiceProvider
- 实现
ApiAdapter
,从而实现ConnectionFactory
- 在
DB
建表,使用默认的UsersConnectionRepository
- 这样就可以在容器中通过
ConnectionFactory
注入Connection
使用了
实现QQ登录
准备工作
https://wiki.connect.qq.com/,到QQ互联官网查看文档,了解QQ登录需要哪些信息,如何获取这些信息,调用哪些接口,传入什么参数,返回什么结果,比较重要的有:
- 获取用户信息
- 获取OpenID
- 获取Access_Token
Api实现
实现Api前,我们先看看AbstractOAuth2ApiBinding
的源码
1 | public abstract class AbstractOAuth2ApiBinding implements ApiBinding, InitializingBean { |
重要的成员变量有两个:
accessToken
:我们知道Api
是第六步,这个Token就是前五步执行完后的令牌,即认证服务器认证后的标志,对于每个线程,每个用户来说,令牌都是不一样的,所以这个是多实例的RestTemplate
:向不同第三方登录接口发送请求的Template
类,使用Restful
形式发送请求
创建QQ接口
1 | public interface QQ { |
接口化,具体实现由QQImpl
实现
创建QQUserInfo的Bean
1 | public class QQUserInfo { |
这里就是从QQ互联的文档中查看的
实现QQ接口
1 | public class QQImpl extends AbstractOAuth2ApiBinding implements QQ { |
这里我们详细说一下,其实注释也很清楚了
首先需要继承AbstractOAuth2ApiBinding
抽象类,实现他的抽象方法getUserInfo()
我们从QQ互联文档可知,使用QQ接口获取对象,需要accessToken
(授权),appId
(qq互联的开发ID),openId
(用户Id),对于accessToken
是由Oauth2Operations
提供的,appId因为是QQ互联注册时给的开发者ID所以肯定是配置的,后面传入的,所以这里我们主要获取openId
。
根据文档,我们需要发送GET请求来获取openId,这一阶段我们在实例化的时候进行
然后我们通过GET请求再获取用户信息,即实现抽象方法getUserInfo()
时进行
这里我使用了阿里的FastJson进行转换,Maven依赖添加一些即可
ServiceProvider实现
我们有了API的QQ实现,对于OAuth2Operations
我们使用默认的OAuth2Template
,所以我们就可以构造出ServiceProvider
了:
1 | public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> { |
这里实现也很简单,继承AbstractOAuth2ServiceProvider
抽象类,并配置QQ互联的两个获取认证的地址即可
ConnectionFactory实现
想要实现ConnectionFactory
,需要两个,ServiceProvider
我们已经有了,就需要实现ApiAdapter
了
实现ApiAdapter
这里我们需实现ApiAdapter
接口,我们之前就了解到,Adapter
是对用户信息进行适配的,将获取到的第三方用户信息赋值到Connection
上,所以代码也很简单,如下:
1 | public class QQAdapter implements ApiAdapter<QQ> { |
有了ApiAdapter
的实现,我们就可以实现ConnectionFactory
了
1 | public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> { |
代码非常简单,就是通过ProviderID
(服务提供商ID),ServiceProvider
(QQServiceProvider
),ApiAdapter
实现即可。
UsersConnectionRepository实现
注入JdbcUsersConnectionRepository
上面绝大部分都实现完毕了,还有一个数据库相关的需要实现,这里我们使用JdbcUsersConnectionRepository
,配置的方式是通过配置SocialConfigurerAdapter
来进行注入:
1 |
|
数据库建映射表
建表语句我们可以通过查看JdbcUsersConnectionRepository
源码位置来获取
1 | create table UserConnection (userId varchar(255) not null, |
向容器添加ConnectionFatory
因为不同的第三方登录方式不同,所以注入方式也不同,这里我们新创建一个配置类,并继承上面写的SocialConfig
配置类:
1 |
|
实现SocialUserDetailsService
这里我们注入了Connection
,但是还有一个问题:我们之前做好了数据库映射,现在可以通过Connection
获取本应用userId
但是我们还需要写一个实现,来通过本应用用户ID获取UserDetails
,就和之前表单登录时一样。
代码很简单,在之前学习表单登录时实现UserDetailsService
,在使用Social
时需要实现SocialUserDetailsService
,其业务逻辑都是和之前一样的
1 |
|
Security中配置Social过滤器
这里我们还需将SocialAuthenticationFilter
加到过滤器链中,首先我们要把SpringSocialConfigurer
注入到容器中,因为这里包含了Social过滤器的配置
1 |
|
这里直接加在之前写的SocialConfig
配置类中就好
然后再SpringSecurity
的配置类中添加social
的配置
1 |
|
这里一些之前的配置我们都进行了省略,最重要就是追加SpringSocial
的配置
前端页面
1 |
|
这里为什么使用"/auth/qq"
呢?我们也没有去配置这个路径,因为SocialAuthenticationFilter
默认拦截/auth
下的路径,而qq
是我们的providerId
我们这样配的话,当请求/auth/qq
时,会先被SocialAuthenticationFilter
过滤器拦截,然后使用我们创建的QQConnectionFactory
来走oauth2
流程+构建Connection
对象。
启动测试
会发现登录失败,非法重定向,到这里我们已经成功了一半了
下一章我们会通过代码,一点点解决问题,直到成功使用QQ登录