责任链模式

责任链简介:为请求创建了一个接收者对象的链。

作用:

        避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

        这里主要说下自己项目当中使用到责任链设计模式的使用场景,针对客户端向后台发起的请求,每个接口都需要做一些校验、比如

  1. 是否登录
  2. 是否完成实名
  3. 是否绑卡(易达金或信用卡任意一张)
  4. 是否绑定绑定本行卡校验
  5. 是否绑定他行卡校验
        并且上述步骤是有先后顺序的,按照12345下来的,每个步骤可以选择校验或不校验,同时,只要有一个校验不满足就返回客户端提示信息。

实现方式:

首先定义一个接口   (对每个步骤约定一个行为规范)

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: zhaoxn
 * @Date: 2022/08/29/21:33
 * @Description:公共校验接口
 */
public interface CommonCheck {
    /**
     * @Date: 2022/11/7 7:28
     * @Description:校验逻辑
     * @Author: zhaoxn
     * @param ckPo:  实体
     * @return boolean
     **/
    boolean verify(CheckPO ckPo);
    /**
     * @Date: 2022/11/7 7:30
     * @Description:是否需要校验
     * @Author: zhaoxn
     * @param level:认证级别
     * @param appCommon:公共字段
     * @return java.lang.String
     **/
    String doCheck(String level, AppCommon appCommon) throws Exception;
}

用户认证级别枚举类

public enum AuthLevelEnum {

    UNBIND_CARD("00","未绑卡"),

    BIND_CARD_ONE("01","绑卡(信用卡与易达金任意一个)"),

    CREDIT_CARD("02","本行信用卡"),

    OTHER_BANK("03","他行卡");

    private String code;

    private String msg;

    AuthLevelEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

1. 登录校验实现类

/**
 * Created with IntelliJ IDEA.
 * @Author: zhaoxn
 * @Date: 2022/08/29/21:39
 * @Description:登录校验
 */
@Order(1)
@Component
public class LoginCheck implements CommonCheck{

    private final static Logger log = LoggerFactory.getLogger(LoginCheck.class);

    @Autowired
    private RedisTemplate<String,Object> redis;

    @Override
    public boolean verify(CheckPO ckPo) {
        //当是否校验标识为Y时进行校验
        return "Y".equals(ckPo);
    }

    @Override
    public String doCheck(String level, AppCommon appCommon) throws Exception {
        log.debug("开始校验登录");
        UserSession userSession = new UserSession(AuthLevelEnum.CREDIT_CARD.getCode());
        //读取登录客户的session
        String jSessionId = "w34sdf9sad8912iheishdfhasdhfsaasdfsfs";
        //判断此人是否命中反洗钱
        String res = (String) redis.opsForValue().get("ANTIMONEY:"+jSessionId);
        if("ANTIMONEY_FLAG".equals(res)){
            throw new RuntimeException("抛出命中反洗钱提示");
        }
        return userSession.getCtfctnLvl();
    }
}

2. 实名校验实现类

/**
 * Created with IntelliJ IDEA.
 * @Author: zhaoxn
 * @Date: 2022/08/29/21:54
 * @Description:是否实名校验
 */
@Order(2)
@Component
public class IsRealNmCheck implements CommonCheck{

    private final static Logger log = LoggerFactory.getLogger(IsRealNmCheck.class);

    @Override
    public boolean verify(CheckPO ckPo) {
        //当是否校验标识为Y时进行校验
        return "Y".equals(ckPo.getIfRealNmUsr());
    }

    @Override
    public String doCheck(String level, AppCommon appCommon) throws Exception {
        log.debug("校验是否实名");
        UserSession userSession = new UserSession(Boolean.TRUE);
        if(!userSession.isIfRealNmUsr()){
            throw new RuntimeException("提示您未实名认证");
        }
        return level;
    }
}

3. 绑卡校验

/**
 * Created with IntelliJ IDEA.
 * @Author: zhaoxn
 * @Date: 2022/08/29/22:04
 * @Description:绑卡校验 (易达金或信用卡任意一张)
 */
@Order(3)
@Component
public class BindCardCheck implements CommonCheck{

    private final static Logger log = LoggerFactory.getLogger(IfRealNmCheck.class);

    @Override
    public boolean verify(CheckPO ckPo) {
        //当是否校验标识为Y时进行校验
        return "Y".equals(ckPo.getIsbdCard());
    }

    @Override
    public String doCheck(String level, AppCommon appCommon) throws Exception {
        log.debug("开始校验绑卡");
        if(AuthLevelEnum.BIND_CARD_ONE.getCode().equals(level)){
            throw new RuntimeException("提示未绑卡");
        }
        return level;
    }
}

4. 绑定本行卡校验

/**
 * Created with IntelliJ IDEA.
 * @Author: zhaoxn
 * @Date: 2022/08/29/22:10
 * @Description:绑定本行卡校验
 */
@Order(4)
@Slf4j
@Component
public class BindCreditCheck implements CommonCheck{

    @Override
    public boolean verify(CheckPO ckPo) {
        //当是否校验标识为Y时进行校验
        return "Y".equals(ckPo.getIsbdXyk());
    }

    @Override
    public String doCheck(String level, AppCommon appCommon) throws Exception {
        log.debug("开始校验绑信用卡");
        if(!(AuthLevelEnum.CREDIT_CARD.getCode().equals(level)) || AuthLevelEnum.CREDIT_CARD.getCode().equals(level)){
            throw new RuntimeException("提示未绑信用卡");
        }
        return level;
    }
}

5. 绑定他行卡校验

/**
 * Created with IntelliJ IDEA.
 * @Author: zhaoxn
 * @Date: 2022/08/29/22:17
 * @Description:绑定他行卡校验
 */
@Order(4)
@Slf4j
@Component
public class BindOtherBankCheck implements CommonCheck{

    private final static Logger log = LoggerFactory.getLogger(IfRealNmCheck.class);

    @Override
    public boolean verify(CheckPO ckPo) {
        //当是否校验标识为Y时进行校验
        return "Y".equals(ckPo.getIsbdYdj());
    }

    @Override
    public String doCheck(String level, AppCommon appCommon) throws Exception {
        log.debug("开始校验绑他行卡");
        if(!(AuthLevelEnum.OTHER_BANK.getCode().equals(level)) || AuthLevelEnum.OTHER_BANK.getCode().equals(level)){
            throw new RuntimeException("提示未绑他行卡校验");
        }
        return level;
    }
}

实现类的执行顺序这里是通过@Order(1)注解来控制的 注解里的值越小优先级越高

切面类定义如下:

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: zhaoxn
 * @Date: 2022/11/08/7:48
 * @Description:权限校验切面
 */
@Aspect
@Component
@Order(-999)
public class PowerCheckAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    private static final Logger logger = LoggerFactory.getLogger(PowerCheckAspect.class);

    @Autowired
    private List<CommonCheck> commonChecks;

    @Autowired
    private DbHxInterfaceMapper dbHxInterfaceMapper;

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RestController)")
    public void dataSubmit() {}

    /**
     * 伪代码表示
     * @param point
     * @throws Throwable
     * @return
     */
    @Around("dataSubmit()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        //使用NotPowerCheck注解的功能直接不进行校验
        String method = "";//假设得到方法名
        if(null!=method.getAnnotation(NotPowerCheck.class)){
            return point.proceed();
        }
        //接口权限表结构    是否绑定信用卡--是否绑定易达金--是否绑卡--是否绑定附属卡--是否绑定借记卡--是否绑定其他行卡--客户认证级别--是否登录--是否实名用户
        //这里考虑某些接口可能没有在接口权限表里预先配置,则放行,通过redis一下,下面有放redis逻辑
        String ifCheck = (String) redisTemplate.opsForValue().get("check:interface:notExists:"+method); //下面往缓存放N值
        //值为N代表不需要权限校验,原因是权限校验表没有该接口的配置信息
        if(ifCheck.equals("N")){
            return point.proceed();
        }
        //取缓存中的接口权限信息,没有就去查db,再放进缓存有效期为次日零点
        String key = "common:interface:exists:"+method;
        Object dbEntity = redisTemplate.opsForValue().get(key);//对象O就是权限校验实体
        if(dbEntity==null){
            //根据接口名查询接口权限
            dbEntity = dbHxInterfaceMapper.selectById(method);
            if(dbEntity==null){  //表中没有配置
                redisTemplate.opsForValue().set("check:interface:notExists:"+method,"N","次日零点", TimeUnit.SECONDS);
                return point.proceed();//表里没配置放行
            }
            //不为空则将接口配置放redis,有效期至第二天0点
            redisTemplate.opsForValue().set(key,dbEntity,"次日零点", TimeUnit.SECONDS);
        }
        logger.debug("-------------------开始校验-------------------");
        AppCommon appCommon = null;
        //此处省略获取每个方法中传过来的appCommon参数    。。。。。。。
        //      appCommon参数处理

        String level = null;
        for (CommonCheck commonCheck: commonChecks){
            if(commonCheck.verify((CheckPO)dbEntity)){ //不同实现类定义了是否走具体校验逻辑标识
                level = commonCheck.doCheck(level,appCommon);
            }
        }
        return point.proceed();
    }

}

                                                                        

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Run,boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值