Social Login, also known as Social Login, means that users of a website can use Tencent QQ, renren, kaixin001, sina weibo, sohu weibo, Tencent weibo, taobao, douban, MSN, Google and other Social media accounts to Login to the website.

Schematic diagram of OAuth2.0 authentication process

  1. Requesting a third-party application
  2. Third-party applications direct user requests to service providers
  3. User consent authorization
  4. The service provider returns the code
  5. The client goes to the service provider for a token according to the code
  6. Returns the token
  7. Obtaining User information

In the standard OAuth2 protocol, steps 1 to 6 are fixed, except for the last step, the user information returned by the disconnected service provider is different. Spring Social has encapsulated steps 1-6 for us.

Use Spring Social

The preparatory work

  1. Apply for individual developers on QQ Internet, get appId and appKey or use SpringForAll to contribute
  2. Configure local host to be added127.0.0.1
  3. The database executes the following SQL
create table UserConnection (userId varchar(255) not null,
	providerId varchar(255) not null,
	providerUserId varchar(255),
	rank int not null,
	displayName varchar(255),
	profileUrl varchar(512),
	imageUrl varchar(512),
	accessToken varchar(512) not null,
	secret varchar(512),
	refreshToken varchar(512),
	expireTime bigint,
	primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);
Copy the code
  1. Project port is set to80port

Introduce the Spring Social module

The module describe
spring-social-core Provides social connection framework and OAuth client support
spring-social-config Providing Java configuration
spring-social-security Some support for social safety
spring-social-web Manage connections to Web applications
! - spring - social related -- -- ><dependency>
Copy the code

The directory structure

  1. ‘API’ defines the public interface for the API binding
  2. ‘config’ some configuration information about QQ
  3. ‘connect’ provides the classes needed to establish a connection with the service provider.

Define the interface that returns user information

public interface QQ {
    /** * Get user information *@return* /
    QQUserInfo getUserInfo(a);
Copy the code

Realize the interface to return user information

public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {

    private static final String QQ_URL_GET_OPENID = "";
    / / (access_token is provided by the parent class)
    private static final String QQ_URL_GET_USER_INFO = "";
    /** * appId configuration file read */
    private String appId;
    /** * openId request QQ_URL_GET_OPENID returns */
    private String openId;
    /** * tool class */
    private ObjectMapper objectMapper = new ObjectMapper();

    /** * the constructor gets openId */
    public QQImpl(String accessToken, String appId) {
        //access_token is carried as a query parameter.
        super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);

        this.appId = appId;

        String url = String.format(QQ_URL_GET_OPENID, accessToken);
        String result = getRestTemplate().getForObject(url, String.class);"【QQImpl】 QQ_URL_GET_OPENID={} result={}", QQ_URL_GET_OPENID, result);

        this.openId = StringUtils.substringBetween(result, "\"openid\":\""."\"}");

    public QQUserInfo getUserInfo(a) {
        String url = String.format(QQ_URL_GET_USER_INFO, appId, openId);
        String result = getRestTemplate().getForObject(url, String.class);"【QQImpl】 QQ_URL_GET_USER_INFO={} result={}", QQ_URL_GET_USER_INFO, result);

        QQUserInfo userInfo = null;
        try {
            userInfo = objectMapper.readValue(result, QQUserInfo.class);
            return userInfo;
        } catch (Exception e) {
            throw new RuntimeException("Failed to obtain user information", e); }}}Copy the code

QQOAuth2Template processes the token information returned by QQ

public class QQOAuth2Template extends OAuth2Template {
    public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
        super(clientId, clientSecret, authorizeUrl, accessTokenUrl);

    protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
        String responseStr = getRestTemplate().postForObject(accessTokenUrl, parameters, String.class);"[QQOAuth2Template] accesstok: responseStr={}" + responseStr);

        String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(responseStr, "&");
        / / using authorization_code access_token
        String accessToken = StringUtils.substringAfterLast(items[0]."=");
        Long expiresIn = new Long(StringUtils.substringAfterLast(items[1]."="));
        String refreshToken = StringUtils.substringAfterLast(items[2]."=");

        return new AccessGrant(accessToken, null, refreshToken, expiresIn);

    /** * error, log debug mode is printed to process text/ HTML data returned by QQ **@return* /
    protected RestTemplate createRestTemplate(a) {
        RestTemplate restTemplate = super.createRestTemplate();
        restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
        returnrestTemplate; }}Copy the code

QQServiceProvider Connects to the service provider

public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {

    /** * get code */
    private static final String QQ_URL_AUTHORIZE = "";
    /** * gets the access_token, which is the token */
    private static final String QQ_URL_ACCESS_TOKEN = "";
    private String appId;

    public QQServiceProvider(String appId, String appSecret) {
        super(new QQOAuth2Template(appId, appSecret, QQ_URL_AUTHORIZE, QQ_URL_ACCESS_TOKEN));
        this.appId = appId;

    public QQ getApi(String accessToken) {

        return newQQImpl(accessToken, appId); }}Copy the code

QQConnectionFactory Factory class for the connection service provider

public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {

    public QQConnectionFactory(String providerId, String appId, String appSecret) {
        super(providerId, new QQServiceProvider(appId, appSecret), newQQAdapter()); }}Copy the code

The QQAdapter ADAPTS to spring Social default return information

public class QQAdapter implements ApiAdapter<QQ> {
    public boolean test(QQ api) {
        return true;

    public void setConnectionValues(QQ api, ConnectionValues values) {
        QQUserInfo userInfo = api.getUserInfo();

        values.setProviderUserId(userInfo.getOpenId());//openId unique identifier

    public UserProfile fetchUserProfile(QQ api) {
        return null;

    public void updateStatus(QQ api, String message) {}}Copy the code

SocialConfig Main class of social configuration

public class SocialConfig extends SocialConfigurerAdapter {

    /** * Social logins **@return* /
    public SpringSocialConfigurer merryyouSocialSecurityConfig(a) {
        String filterProcessesUrl = SecurityConstants.DEFAULT_SOCIAL_QQ_PROCESS_URL;
        MerryyouSpringSocialConfigurer configurer = new MerryyouSpringSocialConfigurer(filterProcessesUrl);
        return configurer;

    /** * Tools to handle the registration process *@param factoryLocator
     * @return* /
    public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator factoryLocator) {
        return newProviderSignInUtils(factoryLocator, getUsersConnectionRepository(factoryLocator)); }}Copy the code
QQAuthConfig for qq return results of some operations
public class QQAuthConfig extends SocialAutoConfigurerAdapter {

    private DataSource dataSource;

    private ConnectionSignUp myConnectionSignUp;

    protectedConnectionFactory<? > createConnectionFactory() {return new QQConnectionFactory(SecurityConstants.DEFAULT_SOCIAL_QQ_PROVIDER_ID, SecurityConstants.DEFAULT_SOCIAL_QQ_APP_ID, SecurityConstants.DEFAULT_SOCIAL_QQ_APP_SECRET);

    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource,
                connectionFactoryLocator, Encryptors.noOpText());
        if(myConnectionSignUp ! =null) {
        returnrepository; }}Copy the code

MerryyouSpringSocialConfigurer custom login and registration links

public class MerryyouSpringSocialConfigurer extends SpringSocialConfigurer {

    private String filterProcessesUrl;

    public MerryyouSpringSocialConfigurer(String filterProcessesUrl) {
        this.filterProcessesUrl = filterProcessesUrl;

    protected <T> T postProcess(T object) {
        SocialAuthenticationFilter filter = (SocialAuthenticationFilter) super.postProcess(object);
        return(T) filter; }}Copy the code

Open SocialAuthenticationFilter filter

    private SpringSocialConfigurer merryyouSpringSocialConfigurer;

    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()// Use form login instead of default httpBasic
                .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)// The URL to jump to if the requested URL requires authentication
                .loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)// Handle the custom login URL in the form
                .permitAll()// None of the above requests require authentication
                .csrf().disable()// Disable CSRD interception
        // The security module is configured separately
Copy the code

The effect is as follows:

The code download

Download it from my Github,…