【Spring Security + OAuth2 + JWT入门到实战】23. 改造基于APP接口社交注册

有些话、适合烂在心里 提交于 2020-03-20 12:24:16

3 月,跳不动了?>>>

简介

上章完成社交登录但是我们是在数据库有用户的情况下登录了,这章解决数据库无该用户数据先完成注册再登录。

流程

看之前浏览器代码如果用户未注册我们会把用户引导到注册页面

注册完成以后访问/social/user连接通过ProviderSignInUtils帮助包到session取用户资料

登录通过以会拿到用户的唯一标识再调用doPostSignUp方法从session拿出用户资料进行绑定再添加到数据库

到这里我们也看到问题那就是整个流程都是基于session完成的,但是我们APP是无session的

改造

改造思路如果第三方信息在数据库查不到数据先保存到Redis等用户注册完我们平台账号拿到userId以后再绑定保存到数据库

创建帮助类AppSingUpUtils:

/**
 * 
 */
package com.spring.security.social;

import java.util.concurrent.TimeUnit;

import com.spring.security.AppSecretException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionData;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;


/**
 * app环境下替换providerSignInUtils,避免由于没有session导致读不到社交用户信息的问题
 *
 */
@Component
public class AppSingUpUtils {

	@Autowired
	private RedisTemplate<Object, Object> redisTemplate;
	
	@Autowired
	private UsersConnectionRepository usersConnectionRepository;
	
	@Autowired
	private ConnectionFactoryLocator connectionFactoryLocator;

	/**
	 * 缓存社交网站用户信息到redis
	 * @param request
	 * @param connectionData
	 */
	public void saveConnectionData(WebRequest request, ConnectionData connectionData) {
		redisTemplate.opsForValue().set(getKey(request), connectionData, 10, TimeUnit.MINUTES);
	}

	/**
	 * 将缓存的社交网站用户信息与系统注册用户信息绑定
	 * @param request
	 * @param userId
	 */
	public void doPostSignUp(WebRequest request, String userId) {
		String key = getKey(request);
		if(!redisTemplate.hasKey(key)){
			throw new AppSecretException("无法找到缓存的用户社交账号信息");
		}
		ConnectionData connectionData = (ConnectionData) redisTemplate.opsForValue().get(key);
		Connection<?> connection = connectionFactoryLocator.getConnectionFactory(connectionData.getProviderId())
				.createConnection(connectionData);
		usersConnectionRepository.createConnectionRepository(userId).addConnection(connection);
		
		redisTemplate.delete(key);
	}

	/**
	 * 获取redis key
	 * @param request
	 * @return
	 */
	private String getKey(WebRequest request) {
		String deviceId = request.getHeader("deviceId");
		if (StringUtils.isBlank(deviceId)) {
			throw new AppSecretException("设备id参数不能为空");
		}
		return "imooc:security:social.connect." + deviceId;
	}

}

 异常处理

/**
 * 
 */
package com.spring.security;

public class AppSecretException extends RuntimeException {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1629364510827838114L;

	public AppSecretException(String msg){
		super(msg);
	}
	
}

 提取hkSocialSecurityConfig类:

package com.spring.security.social;

import com.spring.security.properties.SecurityConstants;
import com.spring.security.social.support.HkSpringSocialConfigurer;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;


@Component
public class SpringSocialConfigurerPostProcessor implements BeanPostProcessor {

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(StringUtils.equals(beanName, "hkSocialSecurityConfig")){
			HkSpringSocialConfigurer config = (HkSpringSocialConfigurer)bean;
			config.signupUrl(SecurityConstants.DEFAULT_SOCIAL_USER_INFO_URL);
			return config;
		}
		return bean;
	}

}
/**
* 获取第三方用户信息的url
*/
String DEFAULT_SOCIAL_USER_INFO_URL = "/social/user";

创建访问/social/user控制层:

/**
 *
 */
package com.spring.security;

import javax.servlet.http.HttpServletRequest;

import com.spring.security.properties.SecurityConstants;
import com.spring.security.social.AppSingUpUtils;
import com.spring.security.social.support.SocialUserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;


@RestController
public class AppSecurityController {

    @Autowired
    private ProviderSignInUtils providerSignInUtils;

    @Autowired
    private AppSingUpUtils appSingUpUtils;

    /**
     * 需要注册时跳到这里,返回401和用户信息给前端
     * @param request
     * @return
     */
    @GetMapping(SecurityConstants.DEFAULT_SOCIAL_USER_INFO_URL)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public SocialUserInfo getSocialUserInfo(HttpServletRequest request) {
        SocialUserInfo socialUsrInfo = new SocialUserInfo();
        Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
        socialUsrInfo.setProviderId(connection.getKey().getProviderId());
        socialUsrInfo.setProviderUserId(connection.getKey().getProviderUserId());
        socialUsrInfo.setNickname(connection.getDisplayName());
        socialUsrInfo.setHeadimg(connection.getImageUrl());
        appSingUpUtils.saveConnectionData(new ServletWebRequest(request), connection.createData());
        return socialUsrInfo;
    }

}
package com.spring.security.social.support;

public class SocialUserInfo {

    private String providerId;

    private String providerUserId;

    private String nickname;

    private String headimg;

    public String getProviderId() {
        return providerId;
    }

    public void setProviderId(String providerId) {
        this.providerId = providerId;
    }

    public String getProviderUserId() {
        return providerUserId;
    }

    public void setProviderUserId(String providerUserId) {
        this.providerUserId = providerUserId;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getHeadimg() {
        return headimg;
    }

    public void setHeadimg(String headimg) {
        this.headimg = headimg;
    }

}

修改用户绑定方法:

    /**
     * 注册和绑定
     *
     * @return
     */
    @PostMapping("/user/regist")
    public void regist(User user, HttpServletRequest request) {
        //这里不管是绑定还是添加  我们都会通过查询或者添加返回用户ID 唯一标
        //我先用用户名当成唯一标识
        String userId = user.getUsername();

        appSingUpUtils.doPostSignUp(new ServletWebRequest(request), userId);
    }

/user/regist和/social/user需要加到不需要认证配置。

启动项目通过昨天方法拿到:

http://127.0.0.1/auth/qq?code=EC04D3F128C9C0B4926384DAAC1A5DCD&state=8883f264-8eae-4c18-a0fb-9de0c61cbcc9

返回401需要APP用到用户去注册我们平台。

通过/user/regist来绑定用户账号:

点Send返回200,看数据库:

现在通过社交openId和providerId去拿token:

访问数据:

 

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