获取qq信息
Spring Social封装了OAuth协议的标准步骤,我们只需要配置第三方应用的认证服务器地址即可,就可以获取到访问令牌Access Token,拿着这个令牌就可以获取到用户信息了,QQ互联的文档中介绍到,要正确获取到用户的基础信息之前,还需要通过Access Token来获取到用户的OpenID,这个OpenID是每一个用户使用QQ登录到你的系统都会产生一个唯一的ID。
qq官方api:qq官方获取用户信息api
获取OpenID
获取OpenID api:
内容 | 说明 |
---|---|
请求URL | https://graph.qq.com/oauth2.0/me |
请求方法 | GET |
请求参数 | access_token |
返回内容 | callback( {“client_id”:“YOUR_APPID”,“openid”:“YOUR_OPENID”} ); |
获取用户信息
根据OpenID获取用户信息api:
内容 | 说明 |
---|---|
请求URL | https://graph.qq.com/user/get_user_info |
请求方法 | GET |
请求参数 | access_token=ACCESS_TOKEN&oauth_consumer_key=APP_ID&openid=OPENID |
返回内容 | 返回内容是JSON格式的字符串 |
代码流程
开发流程如下,对着图片开发相应的模块。
api
qq接口
1 | /** |
QQUserInfo实体
实体类QQUserInfo则是封装了从腾讯服务器获取到的用户基础信息,具体的代码如下所示:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122/**
* QQ用户信息
*
*/
public class QQUserInfo {
/**
* 用户的OpenId
*/
private String openId;
/**
* 返回码
*/
private Integer ret;
/**
* 返回消息,如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码
*/
private String msg;
/**
* 是否丢失0否,1是
*/
"is_lost") (
private Integer isLost;
/**
* 用户在QQ空间的昵称
*/
private String nickname;
/**
* 大小为30×30像素的QQ空间头像URL
*/
"figureurl") (
private String figureUrl30;
/**
* 大小为50×50像素的QQ空间头像URL
*/
"figureurl_1") (
private String figureUrl50;
/**
* 大小为100×100像素的QQ空间头像URL
*/
"figureurl_2") (
private String figureUrl100;
/**
* 大小为40×40像素的QQ头像URL
*/
"figureurl_qq_1") (
private String figureUrlQq40;
/**
* 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有
*/
"figureurl_qq_2") (
private String figureUrlQq100;
/**
* 性别。 如果获取不到则默认返回"男"
*/
private String gender;
/**
* 省份
*/
private String province;
/**
* 城市
*/
private String city;
/**
* 出生年份
*/
private String year;
/**
* 星座
*/
private String constellation;
/**
* 是否是黄钻,0否,1是
*/
"is_yellow_vip") (
private String isYellowVip;
/**
* 是否是会员,0否,1是
*/
private String vip;
/**
* 黄钻等级
*/
"yellow_vip_level") (
private String yellowVipLevel;
/**
* 等级
*/
private String level;
/**
* 是否是黄钻年费VIP,0否,1是
*/
"is_yellow_year_vip") (
private String isYellowYearVip;
}
上面的代码中,使用Jackson将JSON字符串序列化为QQUserInfo实例对象的时候,将带有下划线的字段值映射到了对应的驼峰字段上,使用的Jackson的@JsonProperty注解来完成的。
qq接口实现
1 | /** |
QQImpl类说明:
QQImpl继承了AbstractOAuth2ApiBinding,这在上一篇文章中也介绍了AbstractOAuth2ApiBinding帮助我们完成了一些基础操作,方便我们快速开发。
QQImpl的构造方法中调用了父类AbstractOAuth2ApiBinding的两个参数的构造方法,在父类的构造方法中,我们将第二个参数设置为TokenStrategy.ACCESS_TOKEN_PARAMETER,这样在父类的构造方法中构建RestTemplate对象的时候,就会将accessToken放到请求参数中,如果调用一个参数的父类构造方法,那么它默认的行为是将accessToken放到请求头中,这就和QQ互联要求的请求方式不一样了。
没有将QQImpl标注为Spring Bean,这是因为Spring Bean是单例的,这里的每一个用户应该对应一个QQImpl对象。当用户选择QQ登录的时候,就会去创建一个QQImpl对象,在调用构造方法的时候,就会去事先设定好的链接获取该用户在应用中唯一的OpenID,拿到OpenID后就会调用getUserInfo方法来获取用户信息。
QQServiceProvider
开发完获取用户的QQ信息的接口后,那么接着开发QQServiceProvider,OAuth2Operations是不需要我们开发的,Spring Social提供了OAuth2Template,已经帮我们封装好了OAuth协议规定的基础步骤,我们直接调用即可,在调用之前,需要配置好授权的URL和获取Access Token的URL。
1 | /** |
QQServiceProvider的代码编写还是很简单的,AbstractOAuth2ServiceProvider用到的泛型是API的接口类型,在这里配置了授权的URL和获取Access Token的URL,然后调用AbstractOAuth2ServiceProvider的构造方法就可以获得了Access Token的值,OAuth协议中规定的参数传递等步骤都由Spring Social提供的OAuth2Template来完成了。也许你有一个疑问,在OAuth协议中,在获取授权和获取Access Token的时候都会设置一个参数redirect_uri,但是我们并没有设置这个参数啊?Spring Social是如何帮助我们设置的呢?这里暂时不回答这个问题,请接着往下阅读,后面将会为您解释这个参数设置问题。至此,我们已经开发完了与第三方服务提供商相关的代码,也就是第一幅图的最右边需要的代码。
ConnectionFactory
Connection是一个接口,它有一个实现类OAuth2Connection,该实现类中封装了与用户相关的信息,这些信息,比如DisplayName(显示名称),ProfileUrl(主页地址),ImageUrl(头像地址)等基本信息,这些信息是Spring Social所规定的用户信息(固定字段),我们现在要做的就是将拿到的用户信息转换成OAuth2Connection所封装的用户信息。生成Connection实现类对象需要用到ConnectionFactory工厂,而创建ConnectionFactory对象就需要用到我们开发的QQServiceProvider,还有一个ApiAdapter实现类对象,前者我们已经开发好了,那么现在就需要开发ApiAdapter的实现类,从ApiAdapter这个名称可以看出,它就是一个适配器,负责将从第三方应用拿到的用户基础数据转换成OAuth2Connection的封装的数据,但是进入ApiAdapter的源码看到,我们并不是直接将数据转换成OAuth2Connection封装的属性值,而是设置到ConnectionValues中,后期的转换工作交给Spring Social来完成。分析到这里,我们可以开始编写ApiAdapter实现类的代码了,具体代码如下所示:
1 |
|
这里主要是编写了setConnectionValues方法的代码,将从QQ获取到的数据封装到了ConnectionValues中。现在有了QQServiceProvider和QQAdapter,那么就可以来开发ConnectionFactory的实现类了,这里贴出代码:
1 | public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> { |
写到这里,主要的内容算是写完了,其中UsersConnectionRepository这一块内容封装了对UserConnection表的基础操作,是不需要我们开发的,我们要做的就是将JdbcUsersConnectionRepository配置进来即可,主要代码如下:
1 | /** |
这里使用注解@EnableSocial启用社交登录,并配置了JdbcUsersConnectionRepository,代码中Encryptors.noOpText()表示将用户信息以明文的方式存储到数据库中,也可以以加密的方式进行存储。并将SpringSocialConfigurer的实例对象交给了Spring来管理。最后将SpringSocialConfigurer的对象注入到了BrowserSecurityConfig中,并apply到配置代码中,如下所示:
1 |
|
配置
我们开发一个配置类来接收来自配置文件中的值,定义配置类名称为QQProperties,该类继承SocialProperties,在SocialProperties中,已经存在了appId和appSecret,QQProperties继承了SocialProperties,就相当于已经有了appId和appSecret两个属性,再添加一个providerId属性即可,且设置默认值为qq,代码如下:
QQProperties
1 |
|
SocialProperties基类:
1 |
|
SecurityProperties :
1 |
|
这样设置以后,我们就可以在application.properties中设置appId、appSecret以及providerId了,例如:1
2
3com.lemon.security.social.qq.appId=xxxxxx
com.lemon.security.social.qq.appSecret=xxxxxx
com.lemon.security.social.qq.providerId=xxxxxx
以上最后一个字段名称appId可以替换为app-id,appSecret和providerId同理,Spring读取配置文件是支持横杠转换为驼峰形式的参数。
我们还需要写一个自动配置类,当检测到用户在application.properties中配置了属性com.lemon.security.social.qq.appId后,就应该将QQConnectionFactory实例化,并交给Spring来管理。也就是说,只要开发者开发的系统中配置了属性com.lemon.security.social.qq.appId后,说明该系统就支持QQ登录,那么就应该实例化QQConnectionFactory,且该工厂类是单例的,负责创建与用户信息相关的Connection。自动配置类的代码如下所示:
1 |
|
页面:
1 | <h2>社交登录</h2> |
这里的QQ登录按钮地址为什么是/auth/qq?这是因为Spring Social对社交登录的拦截地址做了默认值,它拦截的请求地址就是/auth,而后面的/qq则是providerId,这是默认规则。
配置url:
我们在配置类SocialConfig中实例化了一个SpringSocialConfigurer的Spring Bean,在这个Bean中直接返回的是SpringSocialConfigurer的实例对象,在这个类的configure方法中,如下所示:
1 |
|
在这个方法中,首先创建了一个SocialAuthenticationFilter对象,最后将其加到了AbstractPreAuthenticatedProcessingFilter这个过滤器之前,在加入之前,调用了postProcess方法,而这个postProcess方法是可以被覆盖掉的,在这里我们可以对SocialAuthenticationFilter进行个性化处理,在个性化处理的过程中将社交登录的拦截路径设置到其中,开发一个配置类,来覆盖一下postProcess方法,代码如下:
1 | /** |
写完这个代码以后,我们在SocialConfig类中就不能在实例化SpringSocialConfigurer了,而是要实例化我们自己写的那个LemonSpringSocialConfigurer类了,在实例化之前,需要修改一些配置,SocialProperties类修改后代码如下:
1 |
|
修改后的SocialConfig类如下所示:
1 | /** |
到这里,我们就解决了不能自定义拦截社交登录的路径问题了,但是要注意的是,当我们没有使用默认的/auth拦截路径的时候,在配置文件中配置的路径一定要和在QQ互联网站上创建的应用配置的回调地址一致,否则还会被提示“回调地址非法”的错误。在这里,我把QQ互联上登记的应用的回调地址改成了http://www.itlemon.cn/authentication/qq,所以我需要在demo项目中添加一个配置com.lemon.security.social.filterProcessesUrl=/authentication,并且将默认的登录页面QQ登录按钮地址改成了/authentication/qq。