述
在使用第三方登录时,有一个常见的场景就是用户登录完成之后,再去绑定/解绑第三方社交账号
绑定也是需要走一下 OAuth 流程,用户授权完成之后,将第三方的用户信息与当前系统的用户信息做一个绑定, 这里和登录不同的地方是, 登录是不知道当前用户信息的,需要先拿到第三方用户信息然后去 userconnection
表里查询系统的用户信息, 而绑定是用户已经登录之后的操作,也就是说已经知道当前业务系统中的用户了,然后授权之后跟当前登录的用户做绑定
解绑操作的话其实就是删除 userconnection
表里的一条数据
Spring Social 对这种场景提供了默认的支持,下面来看一下如何使用
绑定
Spring Social 提供了一个 ConnectController
里面可以查询到当前用户的所有的绑定信息,源码如下:
这里就是从数据库里面找到所有的绑定信息,然后放到Model里面,最后返回一个 connectView()
, 这个方法代码如下:
这里就是返回了一个 connect/status
的视图, 但是这个视图 Social 并没有提供,需要我们自己去实现,所以我们只需要实现这个视图就可以了
connect/status 获取绑定状态
新建类 ConnectionStatusView
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24@Component("connect/status")
public class ConnectionStatusView extends AbstractView {
@Autowired
private ObjectMapper objectMapper;
@Override
protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 拿到所有的绑定信息
Map<String, List<Connection<?>>> connections = (Map<String, List<Connection<?>>>) map.get("connectionMap");
// 最终结果是 [第三方providerId:是否绑定]
Map<String, Boolean> result = new HashMap<>(connections.size());
for (String key : connections.keySet()) {
result.put(key, CollectionUtils.isNotEmpty(connections.get(key)));
}
// 返回给页面
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(result));
}
}
这里我们只需要给前端返回一个是否绑定的标识就好了
可以启动项目请求 connect
看下效果
绑定的实现
绑定同样 Social 也提供了一个接口 POST /connect//{providerId}
源码如下:
这里其实就是重定向到第三方的授权页面, 然后第三方授权成功后的回调是
这个方法主要是往 userconnection
表里面添加数据,绑定完成之后最终也会返回一个视图,名字是 connect/{providerId}Connected
在实现之前,首先写一个html页面用来做绑定1
2
3<form action="/connect/weChat" method="post">
<button type="submit">绑定微信</button>
</form>
只需要一个表单即可,这里首先是前半段 /connect
这个是固定的, 后面的 weChat
是 providerId
然后把视图实现一下, 新建类 ConnectedView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class ConnectView extends AbstractView {
@Autowired
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> result = new HashMap<>(1);
response.setContentType("application/json;charset=UTF-8");
result.put("message", "绑定成功");
response.getWriter().write(objectMapper.writeValueAsString(result));
}
}
这里就是返回了一个绑定成功的信息, 然后这个类并没有加 @Component
注解, 因为所有的第三方绑定成功都是可以重用的
已微信为例,现在视图的名称应该是 connect/weChatConnected
,这个视图我们可以通过 @Bean
的方式注入到 Spring 容器中去, 在 WeChatAutoConfig
中加入以下内容1
2
3
4
5@Bean("connect/weChatConnected")
@ConditionalOnMissingBean(name = "weChatConnectView" )
public View weChatConnectView(){
return new ConnectView();
}
这样就可以了 @ConditionalOnMissingBean
的用法和之前是一样的
这时候可以启动项目,先登陆,然后进去绑定页面,点绑定微信,然后授权,如果返回是我们视图中的信息就成功了, 同时 userconnection
表里面会多一条信息
解绑实现
解绑的接口是 DELETE /connect/{providerId}
他其实就是从 userconnection
中删除一条数据,然后最后他也会返回一个视图,名称是 connect/{providerId}Connect
这里这个视图是可以和上面的绑定的视图用一个, 修改 ConnectView
,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> result = new HashMap<>(1);
response.setContentType("application/json;charset=UTF-8");
if (model.get("connection") == null) {
result.put("message", "解绑成功");
} else {
result.put("message", "绑定成功");
}
response.getWriter().write(objectMapper.writeValueAsString(result));
}
然后 WeChatAutoConfig
修改如下:1
2
3
4
5@Bean({"connect/weChatConnected","connect/weChatConnect"})
@ConditionalOnMissingBean(name = "weChatConnectView" )
public View weChatConnectView(){
return new ConnectView();
}
这里绑定和解绑就是判断一下,有没有 connection
, 然后 @Bean 注入的时候,注入两个就ok了
上面例子都是以微信为例, QQ的可以自己实现一下