要实现绑定与解绑,首先我们需要知道社交账号的绑定状态,绑定就是重新走一下OAuth2
流程,关联当前登录用户(将系统用户和社交账户绑定),解绑就是删除UserConnection
表数据。Spring Social
默认在ConnectController
类上已经帮我们实现了以上的需求。
获取绑定状态
只有数据没有视图这个要我们根据需求来实现
org.springframework.social.connect.web.ConnectController#connectionStatus
/**
* Render the status of connections across all providers to the user as HTML in their web browser.
* @param request the request
* @param model the model
* @return the view name of the connection status page for all providers
*/
@RequestMapping(method=RequestMethod.GET)
public String connectionStatus(NativeWebRequest request, Model model) {
setNoCache(request);
processFlash(request, model);
Map<String, List<Connection<?>>> connections = connectionRepository.findAllConnections();
model.addAttribute("providerIds", connectionFactoryLocator.registeredProviderIds());
model.addAttribute("connectionMap", connections);
return connectView();
}
//返回视图
protected String connectView() {
return getViewPath() + "status";//connect/status
}
实现我们的视图展示
package com.rui.tiger.auth.core.social;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.social.connect.Connection;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 社交账号绑定视图
* @author CaiRui
* @Date 2019-01-20 10:01
*/
@Component("connect/status")
public class TigerConnectionStatusView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, List<Connection<?>>> connections = (Map<String, List<Connection<?>>>) model.get("connectionMap");
//key :社交供应商id value:是否绑定
Map<String,Boolean> bindResult=new HashMap<>();
for(String providerId:connections.keySet()){
bindResult.put(providerId, CollectionUtils.isNotEmpty(connections.get(providerId)));
}
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(bindResult));//返回json
}
}
绑定
源码如下:
/**
* Process a connect form submission by commencing the process of establishing a connection to the provider on behalf of the member.
* For OAuth1, fetches a new request token from the provider, temporarily stores it in the session, then redirects the member to the provider's site for authorization.
* For OAuth2, redirects the user to the provider's site for authorization.
* @param providerId the provider ID to connect to
* @param request the request
* @return a RedirectView to the provider's authorization page or to the connection status page if there is an error
*/
@RequestMapping(value="/{providerId}", method=RequestMethod.POST)
public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {
ConnectionFactory<?> connectionFactory = connectionFactoryLocator.getConnectionFactory(providerId);
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
preConnect(connectionFactory, parameters, request);
try {
return new RedirectView(connectSupport.buildOAuthUrl(connectionFactory, request, parameters));
} catch (Exception e) {
sessionStrategy.setAttribute(request, PROVIDER_ERROR_ATTRIBUTE, e);
return connectionStatusRedirect(providerId, request);
}
}
绑定/解绑公用视图
package com.rui.tiger.auth.core.social;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* 微信wechat社交账号绑定、解绑 成功返回界面 放在配置中管理bean
* getViewPath() + providerId + "Connected";// connect/wechat/Connected
* @author CaiRui
* @Date 2019-01-20 11:19
*/
public class TigerConnectView extends AbstractView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/html;charset=UTF-8");
if (model.get("connections") == null) {
response.getWriter().write("<h3>解绑成功</h3>");
} else {
response.getWriter().write("<h3>绑定成功</h3>");
}
}
}
注入绑定结果视图:
package com.rui.tiger.auth.core.social.wechat.config;
import com.rui.tiger.auth.core.properties.SecurityProperties;
import com.rui.tiger.auth.core.properties.WechatProperties;
import com.rui.tiger.auth.core.social.TigerConnectView;
import com.rui.tiger.auth.core.social.wechat.connect.WechatConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.social.config.annotation.ConnectionFactoryConfigurer;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.ConnectionFactory;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.web.servlet.View;
/**
* 微信登陆配置
* @author CaiRui extends SocialConfigurerAdapter
* @Date 2019-01-12 13:57
*/
@Configuration
@ConditionalOnProperty(prefix = "tiger.auth.social.wechat", name = "app-id")
public class WechatAutoConfiguration extends SocialConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
connectionFactoryConfigurer.addConnectionFactory(createConnectionFactory());
}
private ConnectionFactory<?> createConnectionFactory() {
WechatProperties weixinConfig = securityProperties.getSocial().getWechat();
return new WechatConnectionFactory(weixinConfig.getProviderId(), weixinConfig.getAppId(),
weixinConfig.getAppSecret());
}
/**
* 微信绑定解绑界面视图
* connect/wechatConnected:绑定
* connect/wechatConnect:解绑
* @return
*/
@Bean({"connect/wechatConnect", "connect/wechatConnected"})
@ConditionalOnMissingBean(name = "wechatConnectedView") //可以自定义实现覆盖此默认界面
public View wechatConnectedView(){
return new TigerConnectView();
}
// 后补:做到处理注册逻辑的时候发现的一个bug:登录完成后,数据库没有数据,但是再次登录却不用注册了
// 就怀疑是否是在内存中存储了。结果果然发现这里父类的内存ConnectionRepository覆盖了SocialConfig中配置的jdbcConnectionRepository
// 这里需要返回null,否则会返回内存的 ConnectionRepository
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
return null;
}
}
标准绑定界面:tiger-auth\tiger-auth-browser\src\main\resources\static\tiger-binding.html
<html>
<head>
<meta charset="UTF-8">
<title>绑定 </title>
</head>
<body>
<h2>微信标准绑定页面</h2>
<form action="/connect/wechat" method="post">
<button type="submit">绑定微信</button>
</form>
<h2>QQ标准绑定页面</h2>
<form action="/connect/qq" method="post">
<button type="submit">绑定QQ</button>
</form>
</body>
</html>
调试:
调试qq绑定报下面错误
qq获取授权码文档:http://wiki.connect.qq.com/%e4%bd%bf%e7%94%a8authorization_code%e8%8e%b7%e5%8f%96access_token
经过源码分析发现回调地址:
推测要在互联回调地址也要加 /connect/qq 才可以 等待验证。
解绑
源码如下:
/**
* Remove all provider connections for a user account.
* The user has decided they no longer wish to use the service provider from this application.
* Note: requires {@link HiddenHttpMethodFilter} to be registered with the '_method' request parameter set to 'DELETE' to convert web browser POSTs to DELETE requests.
* @param providerId the provider ID to remove the connections for
* @param request the request
* @return a RedirectView to the connection status page
*/
@RequestMapping(value="/{providerId}", method=RequestMethod.DELETE)
public RedirectView removeConnections(@PathVariable String providerId, NativeWebRequest request) {
ConnectionFactory<?> connectionFactory = connectionFactoryLocator.getConnectionFactory(providerId);
preDisconnect(connectionFactory, request);
connectionRepository.removeConnections(providerId);
postDisconnect(connectionFactory, request);
return connectionStatusRedirect(providerId, request);
}
可以调用postman模拟实现 http://mrcode.cn/connect/qq