述
本文开始,实现一个第三方登录,以QQ为例,首先需要去QQ互联里面,申请开发者认证,然后创建应用,搞到AppId,和AppKey
实现流程
按照上文中的实现流程,先是构建 ServiceProvider
,然后是 ConnectionFactory
然后 Connection
最后交互数据库
这里第三方登录可能在APP和web环境下都会用到,所以写在core项目中
ServiceProvider的实现
API部分
首先 ServiceProvider
里面需要两个东西, 一个是 API
一个是 OAuth2Operations
OAuth2Operations
先用 Spring Social
的默认实现 OAuth2Template
,所以现在我们需要搞定 API
部分
首先新建一个获取QQ用户信息的接口.如下:1
2
3
4
5
6
7
8
9
10public interface QQ {
/**
* 获取用户信息
* @return {@link QQUserInfo}
*/
QQUserInfo getUserInfo() throws IOException;
}
实体类 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@Data
public class QQUserInfo {
/**
* 返回码
*/
private String ret;
/**
* 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
*/
private String msg;
/**
*
*/
private String openId;
/**
* 不知道什么东西,文档上没写,但是实际api返回里有。
*/
private String is_lost;
/**
* 用户在QQ空间的昵称。
*/
private String nickname;
/**
* 性别。 如果获取不到则默认返回”男”
*/
private String gender;
/**
* 省(直辖市)
*/
private String province;
/**
* 市(直辖市区)
*/
private String city;
/**
* 出生年月
*/
private String year;
/**
* 星座
*/
private String constellation;
/**
* 大小为30×30像素的QQ空间头像URL。
*/
private String figureurl;
/**
* 大小为50×50像素的QQ空间头像URL。
*/
private String figureurl_1;
/**
* 大小为100×100像素的QQ空间头像URL。
*/
private String figureurl_2;
/**
* 大小为40×40像素的QQ头像URL。
*/
private String figureurl_qq_1;
/**
* 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100×100的头像,但40×40像素则是一定会有。
*/
private String figureurl_qq_2;
/**
* 也是头像
*/
private String figureurl_qq;
/**
* 头像类型
*/
private String figureurl_type;
/**
* 标识用户是否为黄钻用户(0:不是;1:是)。
*/
private String is_yellow_vip;
/**
* 标识用户是否为黄钻用户(0:不是;1:是)
*/
private String vip;
/**
* 黄钻等级
*/
private String yellow_vip_level;
/**
* 黄钻等级
*/
private String level;
/**
* 标识是否为年费黄钻用户(0:不是; 1:是)
*/
private String is_yellow_year_vip;
}
然后需要一个实现类 QQImpl
,代码如下: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@Slf4j
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
public static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
public static final String URL_GET_USER_INFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";
private String appId;
private String openId;
private ObjectMapper objectMapper = new ObjectMapper();
public QQImpl(String accessToken, String appId){
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
this.appId = appId;
// 有了Appid 和 token之后去取openid
String url = String.format(URL_GET_OPENID, accessToken);
String result = getRestTemplate().getForObject(url, String.class);
log.info("QQ获取用户openid结果:{}", result);
// 返回格式是 callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} ) 字符串所以需要截取一下
this.openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
}
@Override
public QQUserInfo getUserInfo() {
// 有了openid之后就可以获取用户信息了
String url = String.format(URL_GET_USER_INFO, appId, openId);
String result = getRestTemplate().getForObject(url, String.class);
log.info("QQ获取用户信息结果:{}", result);
// 转成对象返回
QQUserInfo qqUserInfo = null;
try {
qqUserInfo = objectMapper.readValue(result, QQUserInfo.class);
// 设置openid到用户信息里
qqUserInfo.setOpenId(openId);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("获取QQ用户信息失败");
}
return qqUserInfo;
}
}
首先这里继承了 AbstractOAuth2ApiBinding
这个就是API的默认实现, 首先获取QQ用户需要 Token
, appid 和 openId
openId需要我们自己去取, 也就是这个类构造里面写的
来看下 AbstractOAuth2ApiBinding
的代码:
他这里的参数一个是accessToken, 一个是RestTemplate, 这俩个都是个全局的变量,所以后面我们不能使用依赖注入, 否则会有线程安全的问题
然后 accessToken 用来放token, 这个类会为我们获取用户信息做一些默认的处理,就是 构造中的 super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
我们子类调用了他的这个构造, 第二个参数表示,将token当做参数传递, 如果不传的话,默认会将 token 放到请求头里面
RestTemplate 就是用来发送Http请求到第三方
在构造中拿到openId之后,就可以使用我们的获取用户的方法,去跟QQ交互,获取用户的信息了.
服务提供商实现
API的部分已经实现了,现在再用一个默认的 OAuth2Template
就可以组成 ServiceProvider
了
新建一个 QQServiceProvider
继承 ServiceProvider
的默认实现 AbstractOAuth2ServiceProvider
1 | public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> { |
这里会重写一个父类的方法是 getApi
,就把我们刚才写的 API 的实现返回就好
然后上面的构造是指定了一个 OAuth2Operations
,用了他的默认实现 OAuth2Template
到这儿服务提供商的开发就完成了,对照上文中的图去看比较容易理解.