【Spring Security + OAuth2 + JWT入门到实战】16. 第三方登录绑定和解绑

蹲街弑〆低调 提交于 2020-03-14 10:15:45

简介

用户登录以后读取用户第三方登录是否绑定,已绑定显示解绑按钮,未绑定显示去绑定按钮

读取用户第三方登录详细源码分析

SpringSocial已经未我们提供了方法ConnectController来读取以后第三方登录信息

在SocialConfig类把ConnectController添加到bean

    // 这个是提供查询社交账户信息服务,绑定服务,等
    @Bean
    public ConnectController connectController(
            ConnectionFactoryLocator connectionFactoryLocator,
            ConnectionRepository connectionRepository) {
        return new ConnectController(connectionFactoryLocator, connectionRepository);
    }

ConnectController

org.springframework.social.connect.web.ConnectController

    public String connectionStatus(NativeWebRequest request, Model model) {
        this.setNoCache(request);
        this.processFlash(request, model);
        Map<String, List<Connection<?>>> connections = this.connectionRepository.findAllConnections();
        model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());
        model.addAttribute("connectionMap", connections);
        return this.connectView();
    }

在connectionStatus方法打断点,启动项目访问:域名/connect

我当前登录的账号绑定了QQ没有绑定weixin,继续下一步看跳默认转到哪

默认跳转:connect/status       这个页面没有我们重写一下

ConnectionStatusView

com.spring.security.social创建ConnectionStatusView类

package com.spring.security.social;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * 读取第三方绑定数据
 */
@Component("connect/status")
public class ConnectionStatusView extends AbstractView {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //取model里封装好的信息
        Map<String, List<Connection<?>>> connections = (Map<String, List<Connection<?>>>) model.get("connectionMap");

        //返回报文  是否绑定
        Map<String, Boolean> r = new HashMap<>();

        //遍历connections里面数据
        for (String key : connections.keySet()) {
            //判断取出来的值是否为null  返回是否
            r.put(key, CollectionUtils.isNotEmpty(connections.get(key)));
        }
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(r));
    }
}

重启项目看是否能返回报文:

绑定

绑定源码,重新走认证流程

/**
	 * 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);
		}
}

创建banding.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>标准绑定页面</title>
</head>
<body>
<h2>标准绑定解绑页面</h2>
<form action="/connect/qq" method="post">
    <button type="submit">绑定QQ</button>
</form>

<form action="/connect/weixin" method="post">
    <button type="submit">绑定微信</button>
</form>
</body>
</html>

启动项目测试

使用表单登录访问页面banding.html

清空数据库表:

点击绑定QQ

如果出现这个错误请把/connect/qq加到QQ互联的回调地址:http://127.0.0.1/auth/qq;http://127.0.0.1/connect/qq

继续登录:

数据已经绑定成功,但是绑定成功以后的跳转报错,现在做一下绑定成功以后的跳转

绑定成功跳转

com.spring.security.social目录创建HkConnectView绑定成功和解绑成功跳转视图类

package com.spring.security.social;

import org.springframework.web.servlet.view.AbstractView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 绑定成功和解绑成功跳转视图  需要放配置中管理bean
 */
public class HkConnectView 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>" + model.get("providerId") + "解绑成功</h3>");
        } else {
            response.getWriter().write("<h3>" + model.get("providerId") + "绑定成功</h3>");
        }
    }
}

注入绑定bean

QQAutoConfig类添加bean配置

    /**
     * QQ绑定解绑界面视图
     * connect/qqConnected:绑定
     * connect/qqConnect:解绑
     *
     * @return
     */
    @Bean({"connect/qqConnect", "connect/qqConnected"})
    @ConditionalOnMissingBean(name = "qqConnectedView") //可以自定义实现覆盖此默认界面
    public HkConnectView qqConnectedView() {
        return new HkConnectView();
    }

WeixinAutoConfig类添加bean配置

   /**
     * weixin绑定解绑界面视图
     * connect/weixinConnected:绑定
     * connect/weixinConnect:解绑
     *
     * @return
     */
    @Bean({"connect/weixinConnect", "connect/weixinConnected"})
    @ConditionalOnMissingBean(name = "weixinConnectedView") //可以自定义实现覆盖此默认界面
    public HkConnectView weixinConnectedView() {
        return new HkConnectView();
    }

启动项目测试,清空数据库表:

绑定就到此结束了。

 

解绑

解绑源码

/**
	 * 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);
	}

解绑和绑定请求地址是一样的,区别:

  • 绑定是POST请求
  • 解绑是DELETE请求

DELETE无法用表单发送请求只能用postman模拟实现

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!