采用工厂模式 定义了一个用户登录处理器工厂类UserLoginProcessorFactory, 定义一个用户认证处理器接口UserAuthenticationProcessor
public interface UserAuthenticationProcessor {
/**
* 要处理的登录类型的processor LoginType:PASSWORD VERIFICATION_CODE
*
* @return {@link com.alibaba.uedmid.udesign.model.constant.LoginType}
*/
LoginType getType();
/**
* 登录逻辑的模版方法,定义整个登录的流程骨架
*
* @param loginDTO 登录信息
* @param afterLogin 认证通过后的后置处理
*/
void login(LoginDTO loginDTO, BiConsumer<LoginUser, TokenInfo> afterLogin);
}
如下代码并实现了spring的监听器接口 容器在启动的时候会从容器中获取定义的用户权限处理的,并根据具类型存到map登录类型存到map中
@Slf4j
public class UserLoginProcessorFactory implements ApplicationListener<ApplicationReadyEvent> {
private static Map<LoginType, UserAuthenticationProcessor> processorFactory;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
ConfigurableApplicationContext applicationContext = event.getApplicationContext();
// 项目启动的时候注册进去
Map<String, UserAuthenticationProcessor> userLoginProcessorMap = applicationContext.getBeansOfType(UserAuthenticationProcessor.class);
if (!userLoginProcessorMap.isEmpty()) {
if (processorFactory == null) {
processorFactory = new HashMap<>();
}
userLoginProcessorMap.forEach((name, processor) -> {
processorFactory.put(processor.getType(), processor);
});
}
}
public static UserAuthenticationProcessor getProcessor(LoginType loginType) {
if (processorFactory == null || processorFactory.isEmpty()) {
log.error("Not Found UserLoginProcessor.loginType:{}", loginType);
throw new IncorrectProcessException("Not Found UserLoginProcessor. ");
}
return processorFactory.get(loginType);
}
用户登录抽象类 定义login的模版方法(定义基本登录骨架) 1.抽象出check方法 2.userNotExistsHandler方法让子类来实现 子类:(2个登录类型,手机号验证码登录,账号密码登录)
@Slf4j
public abstract class AbstractUserAuthenticationProcessor implements UserAuthenticationProcessor {
@Autowired
protected UserRepository userRepository;
@Autowired
protected UserTokenParser userTokenParser;
@Autowired
protected UdesignUserMapper userMapper;
/**
* 对登录的参数做检查
*
* @param loginDTO 登录的参数
*/
public abstract void check(LoginDTO loginDTO);
/**
* 登录逻辑的模版方法,定义整个登录的流程骨架
*
* @param loginDTO 登录信息
* @param afterLogin 认证通过后的后置处理
*/
@Override
public void login(LoginDTO loginDTO, BiConsumer<LoginUser, TokenInfo> afterLogin) {
check(loginDTO);
UdesignUser userInfo = userMapper.findOne(UdesignUserMetaData.telephone.is(loginDTO.getPhone()));
if (userInfo == null) {
log.warn("用户不存在,登录失败. loginDTO:{}", JSON.toJSONString(loginDTO));
userNotExistsHandler(loginDTO);
userInfo = userMapper.findOne(UdesignUserMetaData.telephone.is(loginDTO.getPhone()));
fireLogin(userInfo, afterLogin);
return;
}
fireLogin(userInfo, afterLogin);
}
private void fireLogin(UdesignUser userInfo, BiConsumer<LoginUser, TokenInfo> afterLogin) {
long loginTime = DateUtil.current();
LoginUser loginUser = new LoginUser(userInfo.getUserId(), userInfo.getTelephone(), userInfo.getNickname(), userInfo.getAppName(), loginTime);
String accessToken = userTokenParser.generateToken(loginUser);
UserHelper.tokenStorage(HttpHeaderConstants.X_AUTH_TOKEN, accessToken, (int)(userTokenParser.getExpire() / 1000));
log.info("用户登录成功.user: {}", JSON.toJSONString(loginUser));
if (afterLogin != null) {
TokenInfo tokenInfo = new TokenInfo(HttpHeaderConstants.X_AUTH_TOKEN, accessToken, userTokenParser.getExpire());
afterLogin.accept(loginUser, tokenInfo);
}
}
/**
* 用户不存在时的处理,注意该方法执行完成后会接着执行login方法,在实现的时候要注意适当的return或者抛出异常,不然会无限制的调用login方法造成StackOverFlow
*
* @param loginDTO 登录信息
*/
protected abstract void userNotExistsHandler(LoginDTO loginDTO);
}
策略模式 通过子类的Type来执行不同的策略方法
PasswordAuthenticationProcessor类
@Slf4j
@Service
public class PasswordAuthenticationProcessor extends AbstractUserAuthenticationProcessor {
@Override
public void check(LoginDTO loginDTO) throws IllegalParamException {
UdesignUser userInfo = userMapper.findOne(UdesignUserMetaData.telephone.is(loginDTO.getPhone()));
if (userInfo != null) {
String secret = userInfo.getSecret();
String password = Base64.decodeStr(loginDTO.getPassword(), "UTF-8");
String encryptionPwd = Md5Crypt.md5Crypt(password.getBytes(), secret);
if (!encryptionPwd.equals(userInfo.getPassword())) {
throw new IllegalParamException("密码错误");
}
}
}
@Override
protected void userNotExistsHandler(LoginDTO loginDTO) {
log.warn("用户名密码登录失败.账号不存在. loginDTO: {}", JSON.toJSONString(loginDTO));
throw new MsBizException("账号不存在");
}
@Override
public LoginType getType() {
return LoginType.PASSWORD;
}
}
PhoneVerificationCodeAuthenticationProcessor类
@Service
public class PhoneVerificationCodeAuthenticationProcessor extends AbstractUserAuthenticationProcessor {
@Autowired
private UserNotifier userNotifier;
@Autowired
private UserConfig userConfig;
@Override
public void check(LoginDTO loginDTO) {
userNotifier.checkVerifyCode(loginDTO.getPhone(), VerificationCodeBizType.LOGIN.name(), loginDTO.getVerificationCode());
}
@Override
protected void userNotExistsHandler(LoginDTO loginDTO) {
// 用户不存在,去注册
UserAggregate user = UserAggregate.
builder()
.telephone(loginDTO.getPhone())
.secret(UserHelper.generateRandomSlat())
.nickname(generateNickname())
.appName(UdesignAppName.UDESIGN.name())
.avatar(UserHelper.randomGetAvatar())
.status(UserStatusEnum.AVAILABLE)
.source(loginDTO.getSource())
.build();
user.initQuota(userConfig.getInitQuotaNum());
userRepository.save(user);
}
@Override
public LoginType getType() {
return LoginType.VERIFICATION_CODE;
}
private String generateNickname() {
String nickname = RandomStringUtils.randomAlphanumeric(10);
while (userRepository.getByNickname(nickname) != null) {
nickname = RandomStringUtils.randomAlphanumeric(10);
}
return nickname;
}
}
serivce层登录的实现
@Override
public LoginTokenResponse login(LoginDTO loginDTO) {
LoginTokenResponse response = new LoginTokenResponse();
UserAuthenticationProcessor processor = UserLoginProcessorFactory.getProcessor(loginDTO.getLoginType());
processor.login(loginDTO, (user, tokenInfo) -> {
response.setAccessToken(tokenInfo.getTokenValue());
response.setExpire(tokenInfo.getExpire());
});
return response;
}