[Spring Security + OAuth2 + JWT entry to actual combat] 16. Third party login binding and unbinding

brief introduction

After the user logs in, read whether the user's third-party login is bound. The bound display shows the unbound button, and the unbound display shows the unbound button

Read the detailed source code analysis of user's third-party login

Spring social has not provided the method ConnectController to read the third-party login information

Add ConnectController to bean in SocialConfig class

    // This is to provide social account information query service, binding service, etc
    @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();
    }

At the breakpoint of the connectionStatus method, start project access: domain name / connect

My current login account is bound to QQ and not to weixin. Continue to see where to go by default

Default jump: connect/status this page has not been rewritten

ConnectionStatusView

com.spring.security.social create ConnectionStatusView class

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;

/**
 * Read third-party binding data
 */
@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 {
        //Take the information encapsulated in the model
        Map<String, List<Connection<?>>> connections = (Map<String, List<Connection<?>>>) model.get("connectionMap");

        //Whether the return message is bound
        Map<String, Boolean> r = new HashMap<>();

        //Traverse the data in connections
        for (String key : connections.keySet()) {
            //Judge whether the value is null and return whether
            r.put(key, CollectionUtils.isNotEmpty(connections.get(key)));
        }
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(r));
    }
}

Restart the item to see if the message can be returned:

binding

Bind the source code and go through the authentication process again

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

Create banding.html page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Standard binding page</title>
</head>
<body>
<h2>Standard binding unbound page</h2>
<form action="/connect/qq" method="post">
    <button type="submit">binding QQ</button>
</form>

<form action="/connect/weixin" method="post">
    <button type="submit">Binding WeChat</button>
</form>
</body>
</html>

Start project test

Use the form to log in to the page banding.html

Empty database table:

Click to bind QQ

If this error occurs, please add / connect / QQ to the callback address of QQ interconnection: http://127.0.0.1/auth/qq;http://127.0.0.1/connect/qq

Continue to log in:

The data has been bound successfully, but the jump after the binding is successful reports an error. Now do the jump after the binding is successful

Bind successfully jump

com.spring.security.social directory creates HkConnectView binding successfully, unbinds and skips view class successfully

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;

/**
 * Binding succeeded and unbinding succeeded. Jump view needs to put management bean in configuration
 */
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") + "Untying success</h3>");
        } else {
            response.getWriter().write("<h3>" + model.get("providerId") + "Binding success</h3>");
        }
    }
}

Inject binding bean

Add bean configuration to QQAutoConfig class

    /**
     * QQ Binding and unbinding interface view
     * connect/qqConnected:binding
     * connect/qqConnect:Untying
     *
     * @return
     */
    @Bean({"connect/qqConnect", "connect/qqConnected"})
    @ConditionalOnMissingBean(name = "qqConnectedView") //You can customize the implementation to override this default interface
    public HkConnectView qqConnectedView() {
        return new HkConnectView();
    }

WeixinAutoConfig class add bean configuration

   /**
     * weixin Binding and unbinding interface view
     * connect/weixinConnected:binding
     * connect/weixinConnect:Untying
     *
     * @return
     */
    @Bean({"connect/weixinConnect", "connect/weixinConnected"})
    @ConditionalOnMissingBean(name = "weixinConnectedView") //You can customize the implementation to override this default interface
    public HkConnectView weixinConnectedView() {
        return new HkConnectView();
    }

Start the project test and empty the database table:

This is the end of the binding.

 

Untying

Unbundling source code

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

Unbind is the same as bind request address, with the difference:

  • Binding is a POST request
  • Unbind is a DELETE request

DELETE can't send request with form, can only be implemented with postman simulation

Tags: Programming Spring Java Database Apache

Posted on Fri, 13 Mar 2020 22:58:55 -0400 by serious1234