登录设计模式(工厂模式+模版方法模式+策略模式)

文章介绍了如何使用工厂模式创建UserLoginProcessorFactory,定义了UserAuthenticationProcessor接口和其实现类如PasswordAuthenticationProcessor和PhoneVerificationCodeAuthenticationProcessor,以及在Spring应用中如何通过监听器动态注册处理器。策略模式体现在根据不同登录类型选择不同的验证逻辑。
摘要由CSDN通过智能技术生成

采用工厂模式 定义了一个用户登录处理器工厂类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;
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值