java会员卡的绑定和解绑_前后端分离项目 — SpringSocial 绑定与解绑社交账号如微信、QQ...

1、准备工作

申请QQ、微信相关AppId和AppSecret,这些大家自己到QQ互联和微信开发平台 去申请吧

还有java后台要引入相关的jar包,如下:

org.springframework.security.oauth.boot

spring-security-oauth2-autoconfigure

org.springframework.security.oauth

spring-security-oauth2

2.3.3.RELEASE

org.springframework.boot

spring-boot-starter-security

org.springframework.cloud

spring-cloud-starter-security

org.springframework.cloud

spring-cloud-starter-oauth2

org.springframework.boot

spring-boot-starter-data-redis

org.springframework.boot

spring-boot-starter-jdbc

mysql

mysql-connector-java

org.springframework.social

spring-social-config

1.1.6.RELEASE

org.springframework.social

spring-social-core

1.1.6.RELEASE

org.springframework.social

spring-social-security

1.1.6.RELEASE

org.springframework.social

spring-social-web

1.1.6.RELEASE

io.jsonwebtoken

jjwt

0.9.1

org.apache.commons

commons-lang3

3.7

org.apache.commons

commons-collections4

4.2

commons-beanutils

commons-beanutils

1.9.3

org.springframework.boot

spring-boot-configuration-processor

org.springframework.data

spring-data-mongodb

2.0.9.RELEASE

org.springframework.boot

spring-boot-starter-data-mongodb

2.0.4.RELEASE

com.fasterxml.jackson.core

jackson-core

2.9.6

然后在application.properties里面设置相关配置,如redis、mysql等设置,如下:

spring.datasource.url=

spring.datasource.username=

spring.datasource.password=

spring.datasource.driverClassName=com.mysql.jdbc.Driver

spring.redis.host=127.0.0.1

spring.redis.password=your_pwd

spring.redis.port=6379

spring.redis.timeout=30000

ssb.security.social.register-url=/social/signUp

ssb.security.social.filter-processes-url=/social-login

ssb.security.social.bind-url=https://website/social-bind/qq

ssb.security.social.callback-url=https://website/social-login

ssb.security.social.connect-url=https://website/social-connect

//QQ授权

ssb.security.social.qq.app-id=

ssb.security.social.qq.app-secret=

ssb.security.social.qq.provider-id=qq

//WeChat授权

ssb.security.social.wechat.app-id=

ssb.security.social.wechat.app-secret=

ssb.security.social.wechat.provider-id=wechat

2、分析社交绑定ConnectController类

准备工作做好之后,现在我们开始分析社交绑定,其实spring-social框架里已经自带了spring-social-web,这个jar包里面有个ConnectController.java类,这个类已经帮我们实现了相关绑定与解绑实现方法,问题在于它是基于Session的,所以如果是前后端分离项目使用Session当然应有问题,所以我们要结合Redis来使用,把相关变量都存在Redis中,所以我们上面已经配置好了Redis,我们再来看看Redis配置代码:

@Configuration

public class RestTemplateConfig {

@Bean

public RestTemplate restTemplate(ClientHttpRequestFactory factory){

return new RestTemplate(factory);

}

@Bean

public ClientHttpRequestFactory simpleClientHttpRequestFactory(){

SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();

factory.setReadTimeout(50000);//单位为ms

factory.setConnectTimeout(50000);//单位为ms

return factory;

}

}

3、获取系统当前用户所有社交账号绑定情况

设置好之后,我们来分析一下spring-social-web这个jar包获取社交账号绑定情况,它的请求地址是/connect,代码如下:

@Controller

@RequestMapping({"/connect"})

public class ConnectController implements InitializingBean {

private static final Log logger = LogFactory.getLog(ConnectController.class);

private final ConnectionFactoryLocator connectionFactoryLocator;

private final ConnectionRepository connectionRepository;

private final MultiValueMap, ConnectInterceptor>> connectInterceptors = new LinkedMultiValueMap();

private final MultiValueMap, DisconnectInterceptor>> disconnectInterceptors = new LinkedMultiValueMap();

private ConnectSupport connectSupport;

private final UrlPathHelper urlPathHelper = new UrlPathHelper();

private String viewPath = "connect/";

private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

private String applicationUrl = null;

protected static final String DUPLICATE_CONNECTION_ATTRIBUTE = "social_addConnection_duplicate";

protected static final String PROVIDER_ERROR_ATTRIBUTE = "social_provider_error";

protected static final String AUTHORIZATION_ERROR_ATTRIBUTE = "social_authorization_error";

@Inject

public ConnectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {

this.connectionFactoryLocator = connectionFactoryLocator;

this.connectionRepository = connectionRepository;

}

/** @deprecated */

@Deprecated

public void setInterceptors(List> interceptors) {

this.setConnectInterceptors(interceptors);

}

public void setConnectInterceptors(List> interceptors) {

Iterator var2 = interceptors.iterator();

while(var2.hasNext()) {

ConnectInterceptor> interceptor = (ConnectInterceptor)var2.next();

this.addInterceptor(interceptor);

}

}

public void setDisconnectInterceptors(List> interceptors) {

Iterator var2 = interceptors.iterator();

while(var2.hasNext()) {

DisconnectInterceptor> interceptor = (DisconnectInterceptor)var2.next();

this.addDisconnectInterceptor(interceptor);

}

}

public void setApplicationUrl(String applicationUrl) {

this.applicationUrl = applicationUrl;

}

public void setViewPath(String viewPath) {

this.viewPath = viewPath;

}

public void setSessionStrategy(SessionStrategy sessionStrategy) {

this.sessionStrategy = sessionStrategy;

}

public void addInterceptor(ConnectInterceptor> interceptor) {

Class> serviceApiType = GenericTypeResolver.resolveTypeArgument(interceptor.getClass(), ConnectInterceptor.class);

this.connectInterceptors.add(serviceApiType, interceptor);

}

public void addDisconnectInterceptor(DisconnectInterceptor> interceptor) {

Class> serviceApiType = GenericTypeResolver.resolveTypeArgument(interceptor.getClass(), DisconnectInterceptor.class);

this.disconnectInterceptors.add(serviceApiType, interceptor);

}

@RequestMapping(

method = {RequestMethod.GET}

)

public String connectionStatus(NativeWebRequest request, Model model) {

this.setNoCache(request);

this.processFlash(request, model);

Map>> connections = this.connectionRepository.findAllConnections();

model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());

model.addAttribute("connectionMap", connections);

return this.connectView();

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET}

)

public String connectionStatus(@PathVariable String providerId, NativeWebRequest request, Model model) {

this.setNoCache(request);

this.processFlash(request, model);

List> connections = this.connectionRepository.findConnections(providerId);

this.setNoCache(request);

if(connections.isEmpty()) {

return this.connectView(providerId);

} else {

model.addAttribute("connections", connections);

return this.connectedView(providerId);

}

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.POST}

)

public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

MultiValueMap parameters = new LinkedMultiValueMap();

this.preConnect(connectionFactory, parameters, request);

try {

return new RedirectView(this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters));

} catch (Exception var6) {

this.sessionStrategy.setAttribute(request, "social_provider_error", var6);

return this.connectionStatusRedirect(providerId, request);

}

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET},

params = {"oauth_token"}

)

public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {

try {

OAuth2ConnectionFactory> connectionFactory = (OAuth2ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);

Connection> connection = this.connectSupport.completeConnection(connectionFactory, request);

this.addConnection(connection, connectionFactory, request);

} catch (Exception var5) {

this.sessionStrategy.setAttribute(request, "social_provider_error", var5);

logger.warn("Exception while handling OAuth2 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");

}

return this.connectionStatusRedirect(providerId, request);

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET},

params = {"code"}

)

public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {

try {

OAuth2ConnectionFactory> connectionFactory = (OAuth2ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);

Connection> connection = this.connectSupport.completeConnection(connectionFactory, request);

this.addConnection(connection, connectionFactory, request);

} catch (Exception var5) {

this.sessionStrategy.setAttribute(request, "social_provider_error", var5);

logger.warn("Exception while handling OAuth2 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");

}

return this.connectionStatusRedirect(providerId, request);

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET},

params = {"error"}

)

public RedirectView oauth2ErrorCallback(@PathVariable String providerId, @RequestParam("error") String error, @RequestParam(value = "error_description",required = false) String errorDescription, @RequestParam(value = "error_uri",required = false) String errorUri, NativeWebRequest request) {

Map errorMap = new HashMap();

errorMap.put("error", error);

if(errorDescription != null) {

errorMap.put("errorDescription", errorDescription);

}

if(errorUri != null) {

errorMap.put("errorUri", errorUri);

}

this.sessionStrategy.setAttribute(request, "social_authorization_error", errorMap);

return this.connectionStatusRedirect(providerId, request);

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.DELETE}

)

public RedirectView removeConnections(@PathVariable String providerId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

this.preDisconnect(connectionFactory, request);

this.connectionRepository.removeConnections(providerId);

this.postDisconnect(connectionFactory, request);

return this.connectionStatusRedirect(providerId, request);

}

@RequestMapping(

value = {"/{providerId}/{providerUserId}"},

method = {RequestMethod.DELETE}

)

public RedirectView removeConnection(@PathVariable String providerId, @PathVariable String providerUserId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

this.preDisconnect(connectionFactory, request);

this.connectionRepository.removeConnection(new ConnectionKey(providerId, providerUserId));

this.postDisconnect(connectionFactory, request);

return this.connectionStatusRedirect(providerId, request);

}

protected String connectView() {

return this.getViewPath() + "status";

}

protected String connectView(String providerId) {

return this.getViewPath() + providerId + "Connect";

}

protected String connectedView(String providerId) {

return this.getViewPath() + providerId + "Connected";

}

protected RedirectView connectionStatusRedirect(String providerId, NativeWebRequest request) {

HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);

String path = "/connect/" + providerId + this.getPathExtension(servletRequest);

if(this.prependServletPath(servletRequest)) {

path = servletRequest.getServletPath() + path;

}

return new RedirectView(path, true);

}

public void afterPropertiesSet() throws Exception {

this.connectSupport = new ConnectSupport(this.sessionStrategy);

if(this.applicationUrl != null) {

this.connectSupport.setApplicationUrl(this.applicationUrl);

}

}

private boolean prependServletPath(HttpServletRequest request) {

return !this.urlPathHelper.getPathWithinServletMapping(request).equals("");

}

private String getPathExtension(HttpServletRequest request) {

String fileName = this.extractFullFilenameFromUrlPath(request.getRequestURI());

String extension = StringUtils.getFilenameExtension(fileName);

return extension != null?"." + extension:"";

}

private String extractFullFilenameFromUrlPath(String urlPath) {

int end = urlPath.indexOf(63);

if(end == -1) {

end = urlPath.indexOf(35);

if(end == -1) {

end = urlPath.length();

}

}

int begin = urlPath.lastIndexOf(47, end) + 1;

int paramIndex = urlPath.indexOf(59, begin);

end = paramIndex != -1 && paramIndex < end?paramIndex:end;

return urlPath.substring(begin, end);

}

private String getViewPath() {

return this.viewPath;

}

private void addConnection(Connection> connection, ConnectionFactory> connectionFactory, WebRequest request) {

try {

this.connectionRepository.addConnection(connection);

this.postConnect(connectionFactory, connection, request);

} catch (DuplicateConnectionException var5) {

this.sessionStrategy.setAttribute(request, "social_addConnection_duplicate", var5);

}

}

private void preConnect(ConnectionFactory> connectionFactory, MultiValueMap parameters, WebRequest request) {

Iterator var4 = this.interceptingConnectionsTo(connectionFactory).iterator();

while(var4.hasNext()) {

ConnectInterceptor interceptor = (ConnectInterceptor)var4.next();

interceptor.preConnect(connectionFactory, parameters, request);

}

}

private void postConnect(ConnectionFactory> connectionFactory, Connection> connection, WebRequest request) {

Iterator var4 = this.interceptingConnectionsTo(connectionFactory).iterator();

while(var4.hasNext()) {

ConnectInterceptor interceptor = (ConnectInterceptor)var4.next();

interceptor.postConnect(connection, request);

}

}

private void preDisconnect(ConnectionFactory> connectionFactory, WebRequest request) {

Iterator var3 = this.interceptingDisconnectionsTo(connectionFactory).iterator();

while(var3.hasNext()) {

DisconnectInterceptor interceptor = (DisconnectInterceptor)var3.next();

interceptor.preDisconnect(connectionFactory, request);

}

}

private void postDisconnect(ConnectionFactory> connectionFactory, WebRequest request) {

Iterator var3 = this.interceptingDisconnectionsTo(connectionFactory).iterator();

while(var3.hasNext()) {

DisconnectInterceptor interceptor = (DisconnectInterceptor)var3.next();

interceptor.postDisconnect(connectionFactory, request);

}

}

private List> interceptingConnectionsTo(ConnectionFactory> connectionFactory) {

Class> serviceType = GenericTypeResolver.resolveTypeArgument(connectionFactory.getClass(), ConnectionFactory.class);

List> typedInterceptors = (List)this.connectInterceptors.get(serviceType);

if(typedInterceptors == null) {

typedInterceptors = Collections.emptyList();

}

return typedInterceptors;

}

private List> interceptingDisconnectionsTo(ConnectionFactory> connectionFactory) {

Class> serviceType = GenericTypeResolver.resolveTypeArgument(connectionFactory.getClass(), ConnectionFactory.class);

List> typedInterceptors = (List)this.disconnectInterceptors.get(serviceType);

if(typedInterceptors == null) {

typedInterceptors = Collections.emptyList();

}

return typedInterceptors;

}

private void processFlash(WebRequest request, Model model) {

this.convertSessionAttributeToModelAttribute("social_addConnection_duplicate", request, model);

this.convertSessionAttributeToModelAttribute("social_provider_error", request, model);

model.addAttribute("social_authorization_error", this.sessionStrategy.getAttribute(request, "social_authorization_error"));

this.sessionStrategy.removeAttribute(request, "social_authorization_error");

}

private void convertSessionAttributeToModelAttribute(String attributeName, WebRequest request, Model model) {

if(this.sessionStrategy.getAttribute(request, attributeName) != null) {

model.addAttribute(attributeName, Boolean.TRUE);

this.sessionStrategy.removeAttribute(request, attributeName);

}

}

private void setNoCache(NativeWebRequest request) {

HttpServletResponse response = (HttpServletResponse)request.getNativeResponse(HttpServletResponse.class);

if(response != null) {

response.setHeader("Pragma", "no-cache");

response.setDateHeader("Expires", 1L);

response.setHeader("Cache-Control", "no-cache");

response.addHeader("Cache-Control", "no-store");

}

}

}

上面就是ConnectController的源码了,我们现在分析一下获取当前用户社交绑定情况的方法:

@RequestMapping(

method = {RequestMethod.GET}

)

public String connectionStatus(NativeWebRequest request, Model model) {

this.setNoCache(request);

this.processFlash(request, model);

Map>> connections = this.connectionRepository.findAllConnections();

model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());

model.addAttribute("connectionMap", connections);

return this.connectView();

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET}

)

public String connectionStatus(@PathVariable String providerId, NativeWebRequest request, Model model) {

this.setNoCache(request);

this.processFlash(request, model);

List> connections = this.connectionRepository.findConnections(providerId);

this.setNoCache(request);

if(connections.isEmpty()) {

return this.connectView(providerId);

} else {

model.addAttribute("connections", connections);

return this.connectedView(providerId);

}

}

对了,就是这两个方法,前面第一个方法请求的地址是:/connect(需要用户登录) 这个地址是获取当前用户所有社交账号绑定情况,第二个方法请求的地址是:/connect/{providerId}(需要用户登录) 这个地址是获取某个社交账号绑定情况,如/connect/qq,所以我们要获取当前用户绑定的所有社交账号绑定情况,使用的是第一个方法,但是现在有个问题,获取完之后 它是直接跳转页面到/connect/status,当然这不是我们想要的,我们要修改这个类,比如地址换成/socialConnect,这个换成自己的就好,然后我们来改下这个方法,如下:

@RequestMapping(

method = {RequestMethod.GET}

)

public ResponseEntity> connectionStatus(NativeWebRequest request, Model model) throws JsonProcessingException {

this.setNoCache(request);

this.processFlash(request, model);

Map>> connections = this.connectionRepository.findAllConnections();

model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());

model.addAttribute("connectionMap", connections);

Map result = new HashMap();

for (String key : connections.keySet()){

result.put(key, org.apache.commons.collections.CollectionUtils.isNotEmpty(connections.get(key)));

}

return ResponseEntity.ok(objectMapper.writeValueAsString(result));

}

改好的代码直接返回Json数据给前端,而不是跳转页面,完美解决了前后端分离项目问题,好了,我们使用postman发送请求测试看看:

3694338860-5bace2ed938d9_articlex.png

如图所示,我们成功获取当前登录用户所有社交账号绑定情况了(为什么这里只有qq和微信?社交账号的类型是你application.proterties里面配置的)。

4、绑定社交账号

好了,我们来看看绑定社交账号的方法:

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.POST}

)

public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

MultiValueMap parameters = new LinkedMultiValueMap();

this.preConnect(connectionFactory, parameters, request);

try {

return new RedirectView(this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters));

} catch (Exception var6) {

this.sessionStrategy.setAttribute(request, "social_provider_error", var6);

return this.connectionStatusRedirect(providerId, request);

}

}

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET},

params = {"code"}

)

public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {

try {

OAuth2ConnectionFactory> connectionFactory = (OAuth2ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);

Connection> connection = this.connectSupport.completeConnection(connectionFactory, request);

this.addConnection(connection, connectionFactory, request);

} catch (Exception var5) {

this.sessionStrategy.setAttribute(request, "social_provider_error", var5);

logger.warn("Exception while handling OAuth2 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");

}

return this.connectionStatusRedirect(providerId, request);

}

现在来分析 下这两个 方法的作用,第一个方法请求的地址是:POST /connect/{providerId}(需要登录) ,第二个方法请求地址是:GET /connect/{providerId}?code=&state=(需要登录),第一个方法是获取社交授权连接地址(这个是你自己社交登录时候封装好的,这里我不打算详细讲解,后面课程再放出来吧)比如qq的授权地址:https://graph.qq.com/oauth2.0…,这样当你授权成功之后就回调到了第二个方法里面,顺便把code和state原样返回过来,这一套绑定机制都是基于session的,下面我们来分析看下他是如何实现的。

我们以微信为例,首先我们发送一个POST请求/connect/wechat,因为你已经登录了,所以后台可以获取当前user是谁,然后就获取到请求的链接:https://open.weixin.qq.com/co…,最后就是跳转到这个链接上面去。这是第一个方法的作用,接下来我们分析第二个方法。

请求上面的链接之后就是跳转到微信扫码的页面,如下所示:

扫完之后立马就跳到上面链接redirect_uri地址上面去,也就是现在的第二个方法上面,而且是带着state和code两个参数,这时候后台开始验证你回传过来的state值是不是匹配的,不匹配就报错并且跳转到出错页面,匹配的话就往下走,并且通过code获取SpringSecurity OAuth相关社交用户信息并保存到数据库中,这就是code和state的作用,验证和获取完之后就可以,这样你就绑定成功了,最后跳转到/connected/wechat页面了,这样就结束了绑定功能了。

那么我们前后端分离项目要使用这套机制,我们必须改一下他的源码了。

首先第一个方法,我们要把userId保存到以state的redis键值对中,也就是:{state:userId},然后以JSON的格式返回社交授权的链接给前台,这是第一个方法要修改的思路。

然后第二个方法,是社交授权链接返回回来的,因为前后端分离项目session就无法使用了,所以要获取用户信息必须通过上面redis保存的{state:userId},来获取用户id。再一个我们通过code获取社交用户信息,两个数据都获取了,这个时候我们就可以安心的把社交用户信息保存到数据库中(这里的通过state从redis中获取userId,其实也是一种验证state的方式,你想想可是呢!),最后就跳转到你想要的页面就好了,下面就是修改后的代码了,可以看看:

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.POST}

)

public ResponseEntity> connect(@PathVariable String providerId,

NativeWebRequest request) {

HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);

Principal user = nativeRequest.getUserPrincipal();

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

MultiValueMap parameters = new LinkedMultiValueMap();

this.preConnect(connectionFactory, parameters, request);

try {

String social_connect_url = this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters);

String state = (String) this.sessionStrategy.getAttribute(request, "oauth2State");

this.sessionStrategy.removeAttribute(request, "oauth2State");

//把userId以state为key的形式保存到redis中

socialRedisHelper.saveStateUserId(state, user.getName());

//返回社交链接地址

return ResponseEntity.ok(social_connect_url);

} catch (Exception var6) {

this.sessionStrategy.setAttribute(request, "social_provider_error", var6);

logger.info(var6.getMessage());

return null;

}

}

//辅助方法1

protected String callbackUrl(NativeWebRequest request) {

if (this.callbackUrl != null) {

return this.callbackUrl;

} else {

HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);

return this.applicationUrl != null ? this.applicationUrl + this.connectPath(nativeRequest) : nativeRequest.getRequestURL().toString();

}

}

//辅助方法2

private String connectPath(HttpServletRequest request) {

String pathInfo = request.getPathInfo();

return request.getServletPath() + (pathInfo != null ? pathInfo : "");

}

//回调方法

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.GET},

params = {"code"}

)

public void oauth2Callback(@PathVariable String providerId,

NativeWebRequest request,

HttpServletResponse response) {

try {

//ConnectController是先保存在session里面,然后回调从session里面取出来校验

//我现在是通过redis保存state 的 userId,这样就相当于校验了state

String state = request.getParameter("state");

String code = request.getParameter("code");

OAuth2ConnectionFactory> connectionFactory = (OAuth2ConnectionFactory) this.connectionFactoryLocator.getConnectionFactory(providerId);

AccessGrant accessGrant = connectionFactory.getOAuthOperations().exchangeForAccess(code, this.callbackUrl(request), null);

Connection> connection = connectionFactory.createConnection(accessGrant);

//从redis中获取userid

String userId = socialRedisHelper.getStateUserId(state);

//保存到数据库中

jdbcConnectionRepository.createConnectionRepository(userId).addConnection(connection);

//跳转页面到前台任何你想设置的地址

response.sendRedirect(connectUrl);

} catch (Exception ex) {

logger.info(ex.getMessage());

}

}

这样你就完成了后台绑定相关工作,那么我把前端相关代码也放出来大家看下吧:

gotoBind(type){

let url = `${this.$url}/socialConnect/${type}`;

this.$post(url)

.then(res=>{

if(res.code == 0){

this.openWindow(res.data.redirect_uri)

}

})

},

openWindow(url){

let sf_H = 550;

let sf_W = 720;

var iTop = (window.screen.height-30 -sf_H)/2; //获得窗口的垂直位置;

var iLeft = (window.screen.width-10 -sf_W)/2; //获得窗口的水平位置;

let s = window.open(url,"social_bind_form",'height='+sf_H+

', width='+sf_W+',top='+iTop+',left='+iLeft+'toolbar=no, menubar=no, scrollbars=no, status=no, location=yes, resizable=yes');

},

上面是获取社交绑定地址并跳转,下面是回调成功之后关闭对话框并刷新的页面代码。

import {mapActions,mapState} from 'vuex'

export default {

data(){

return{

}

},

created(){

window.close();

opener.location.reload();

},

methods:{

}

}

我们来演示一下:

2144a248f5a3e9113ad154daa2663a29.png

349c4fcfa39e632baf632e912a9b18ad.png

6dcd3fa09ee6435c90677c0e75128d3f.png

5、解绑社交账号

绑定社交账号已经成功了,现在我们来看一下如何解绑社交账号吧,我们先看下源码是如何实现的,如下

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.DELETE}

)

public RedirectView removeConnections(@PathVariable String providerId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

this.preDisconnect(connectionFactory, request);

this.connectionRepository.removeConnections(providerId);

this.postDisconnect(connectionFactory, request);

return this.connectionStatusRedirect(providerId, request);

}

@RequestMapping(

value = {"/{providerId}/{providerUserId}"},

method = {RequestMethod.DELETE}

)

public RedirectView removeConnection(@PathVariable String providerId, @PathVariable String providerUserId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

this.preDisconnect(connectionFactory, request);

this.connectionRepository.removeConnection(new ConnectionKey(providerId, providerUserId));

this.postDisconnect(connectionFactory, request);

return this.connectionStatusRedirect(providerId, request);

}

第一个方法请求地址是:Delete /connect/{providerId}(需登录),第二个方法请求地址是:Delete /connect/{providerId}/{providerUserId}(需登录),注意这里的providerUserId其实就是社交用户id,比如微信的openId,第一个方法是根据登录的userId和providerId来删除数据库中绑定的社交用户数据,第二个方法是根据登录的userId和providerId还有providerUserId来删除数据库中绑定的社交用户数据,这两个 方法都有相同的一点就是跳转到删除之后的页面,所以我们只要把跳转页面以JSON的形式返回给前端就好,下面就是修改后的代码:

@RequestMapping(

value = {"/{providerId}"},

method = {RequestMethod.DELETE}

)

public ResponseEntity> removeConnections(@PathVariable String providerId, NativeWebRequest request) {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

this.preDisconnect(connectionFactory, request);

this.connectionRepository.removeConnections(providerId);

this.postDisconnect(connectionFactory, request);

return ResponseEntity.ok("success");

}

@RequestMapping(

value = {"/{providerId}/{providerUserId}"},

method = {RequestMethod.DELETE}

)

public ResponseEntity> removeConnection(@PathVariable String providerId,

@PathVariable String providerUserId,

NativeWebRequest request) throws IOException {

try {

ConnectionFactory> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);

this.preDisconnect(connectionFactory, request);

this.connectionRepository.removeConnection(new ConnectionKey(providerId, providerUserId));

this.postDisconnect(connectionFactory, request);

} catch (Exception ex) {

logger.info(ex.getMessage());

}

return ResponseEntity.ok("success");

}

我们再把前端代码贴出来:

gotoUnBind(type){

let url = `${this.$url}/socialConnect/${type}`;

this.$delete(url)

.then(res=>{

if(res.code == 0){

this.$Message.success('解绑成功!')

location.reload();

}

})

},

6、总结:

1、只要把思路理清楚了,其实修改成自己想要的代码就不难

2、注意ConnectController代码是基于Session的,所以你必须要登录的情况下才能使用

3、redis的使用在这里发挥到了一定作用,所以说前后端分离项目离不开redis

如果你觉得帮助到你了,可以打赏我更有动力来更新文章

7、引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值