浏览器注册模式
前面的我们社交登录都是基于用户的社交信息都已经存在于数据库中了, 如果用户首次登录该怎么办呢?回顾下我们的浏览器注册逻辑。
具体详细步骤 社交注册
1.当第一次用社交用户信息登录,会默认跳到我们自定义的注册界面
2.在跳到注册页之前,会把第三方用户信息放到session中
3.默认提供一个 /social/user 请求可以拿到我们的用户信息展示用户,注册或绑定后会拿到用户的唯一标识, 调用social数据库服务,把关联信息写入数据库中,完成注册 。
4.再次登录,数据库中有用户信息,则登录成功
上面的流程针对app的实现有问题,因为用户信息都是基于session的。下面我们来进行改造
改造思路
1.把从session存储,放到reids中存储,传入设备id
2.其它流程同上面类似
基于redis的社交用户信息保存工具类 AppSignUpUtils
package com.rui.tiger.auth.app.social;
import com.rui.tiger.auth.app.AppConstants;
import com.rui.tiger.auth.app.exceptions.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.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
/**
* 社交用户和系统用户的关系注册
* ProviderSignInUtils 模拟其中部分的功能
* @author CaiRui
* @date 2019-04-30 08:28
*/
@Component
public class AppSignUpUtils {
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private UsersConnectionRepository usersConnectionRepository;
@Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
/**
* 保存社交信息到redis
* @param request
* @param connectionData
*/
public void saveConnectionData(ServletWebRequest request, ConnectionData connectionData) {
redisTemplate.opsForValue().set(buildKey(request), connectionData);
}
/**
* 社交用户信息入库
* @param request
* @param userId
*/
public void doPostSignUp( String userId,WebRequest request) {
String key = buildKey(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);
}
private String buildKey(WebRequest request) {
String deviceId = request.getHeader(AppConstants.DEFAULT_HEADER_DEVICE_ID);
if (StringUtils.isBlank(deviceId)) {
throw new AppSecretException("设备id参数不能为空");
}
return "tiger:security:social.connect." + deviceId;
}
}
然后创建SpringSocialConfigurerPostProcessor ,bean生命周期初始化后置处理
package com.rui.tiger.auth.app;
import com.rui.tiger.auth.core.social.TigerSpringSocialConfigurer;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 后置处理器 springbean 初始化之前和之后
*
* @author CaiRui
* @date 2019-04-30 08:42 BeanPostProcessor
*/
@Component
public class SpringSocialConfigurerPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (StringUtils.equals(beanName, "tigerSpringSocialConfigurer")) {
TigerSpringSocialConfigurer configurer = (TigerSpringSocialConfigurer)bean;
configurer.signupUrl("/social/signUp");//更换浏览器的默认注册
return configurer;
}
return bean;
}
}
springbean生命周期关系如下:
注册控制器 AppSecurityController
package com.rui.tiger.auth.app.controller;
import com.rui.tiger.auth.app.social.AppSignUpUtils;
import com.rui.tiger.auth.core.social.SocialUserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionData;
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;
import javax.servlet.http.HttpServletRequest;
/**
* @author CaiRui
* @date 2019-04-30 08:45
*/
@RestController
public class AppSecurityController {
@Autowired
private ProviderSignInUtils providerSignInUtils;
@Autowired
private AppSignUpUtils appSignUpUtils;
@GetMapping("/social/signUp")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public SocialUserInfo getSocialUserInfo(HttpServletRequest request) {
SocialUserInfo userInfo = new SocialUserInfo();
Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
ConnectionData connectionData=connection.createData();
userInfo.setProviderId(connection.getKey().getProviderId());
userInfo.setProviderUserId(connection.getKey().getProviderUserId());
userInfo.setNickName(connection.getDisplayName());
userInfo.setHeadImg(connection.getImageUrl());
appSignUpUtils.saveConnectionData(new ServletWebRequest(request), connectionData);
return userInfo;
}
}
用户注册的地方也要改成我们的工具类来保存 com.rui.tiger.auth.demo.controller.UserController#regist
/**
* 社交注册
*/
@PostMapping("/regist")
public void regist(UserVo user, HttpServletRequest request){
// 不管是注册用户还是绑定用户,都会拿到一个用户唯一标识
String username=user.getUsername();
//这里处理绑定或注册用户逻辑
//进行系统用户和社交用户入库动作
appSignUpUtils.doPostSignUp(username,new ServletWebRequest(request));
//providerSignInUtils.doPostSignUp(username, new ServletWebRequest(request));
}
ok 代码完成我们来测试下,测试前记得关闭我们的自动注册社交用户的服务
拿授权码的逻辑通前面类似,断点调试,在浏览器和app之间切换。
1.现将我们库中已有的社交信息修改,就是不存在
2.浏览器环境模拟拿到请求授权地址和授权码
授权码:8C54B950B35140EAA55AF6D9D6FF2F3E
3. 切换到APP环境 postman发送请求,
我们看下redis中是否有社交信息,可以看到redis工具类已经生效
4.用户信息已经达到 我们来注册
ok社交信息入库成功
ok 注册逻辑重构完成,下章我们来完成令牌配置