Redis相关——套餐购买后的权限校验问题

Redis相关——套餐购买后的权限校验问题

前言

本文是我在工作上的需求衍生,因为案例比较经典,遂加以记录,希望以后能对其他类似工作具有参考性!

需求描述

在用户购买产品套餐后,会拥有调用相关接口的权限,此时,权限校验的机制是怎样的呢?如何设计与实现会比较合理?

需求点梳理

  1. 获取用户权限的实现原理是什么?
    首先需要在开放平台上获取用户的所有订单详情,判断用户的购买套餐是否是对应的产品,然后需要判断产品的使用期限是否>0,如果均满足条件,则判断用户权限是true,如果用户的所有订单详情均不满足,则为false;如果查询订单为空,证明用户从没有下过单,为false;
  2. 由于调用的接口可能非常多,均需要权限校验,我们如何使用简化代码来满足多个接口的校验?
    可以添加自定义注解与拦截器,去校验用户是否具有权限;
  3. 为了减少用户频繁调用其他平台服务,我们可以将校验结果设定一个时间期限,缓存到redis中去;
  4. Redis中的记录什么时候需要消失呢?
    需要考虑到,除了过期以外,在用户去往订阅页面下单的时候也需要将记录清除,否则,会发生用户即使下过单,但也还是无法获取到权限的事故!

方案设计

请添加图片描述

代码实现

  1. 构建权限校验注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public  @interface VisualToBuyToken {
    // 校验token
    boolean validate() default true;
}
  1. 在拦截器中调用校验权限方法
@Component
@Slf4j
@NonNullApi
public class VisualAuthorizationInterceptor extends HandlerInterceptorAdapter {

    @CloudReference(service="business")
    private SubscribeService subscribeService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        VisualToBuyToken annotation;
        if (handler instanceof HandlerMethod) {
            //先获取方法上的注解
            annotation = ((HandlerMethod) handler).getMethodAnnotation(VisualToBuyToken.class);
            //如果方法没有注解,则去类上去获取 如果方法有则使用方法的注解,方法的注解优先级高于类的注解
            if (annotation == null) {
                annotation = ((HandlerMethod) handler).getBeanType().getAnnotation(VisualToBuyToken.class);
            }
        }
        // 如果请求的不是方法 则直接跳过当前拦截器
        else {
            return true;
        }


        //没有声明需要权限,或者声明不验证权限
        if (annotation == null || !annotation.validate()) {
            return true;
        }

        ......

        //校验用户是否有权限
        if (!subscribeService.verVisualToBuy(loginUser)) {
            throw new BaseBizException(ResponseEnum.ERROR_NO_VISUAL_AUTH.getErrorCode(), ResponseEnum.ERROR_NO_VISUAL_AUTH.getErrorInfo());
        }

        ......

        return true;
    }
}
  1. verVisualToBuy方法校验权限
@Override
public Boolean verVisualToBuy(LoginUser loginUser) {

    // 首先从redis中获取
    Boolean visualToBuyAuth = LightDataConstant.HS_CACHE.get(LightDataConstant.VISUAL_TO_BUY + loginUser.getAuthId() , Boolean.class);
    if (visualToBuyAuth != null) {
        return visualToBuyAuth;
    }

    // 若redis中为空,执行查询逻辑并缓存到redis中去,缓存时间设置为1小时
    visualToBuyAuth = getVisualToBuy(loginUser);
    LightDataConstant.HS_CACHE.put(LightDataConstant.VISUAL_TO_BUY + loginUser.getAuthId(), visualToBuyAuth, 3600);
    return visualToBuyAuth;
}
  1. 在订阅时开启多线程删除缓存,这是因为需要不影响其他订阅过程的展开~,需要注意,删除之前首先判断是否存在记录,若没有,则不删除直接跳过
// 开启多线程进行redis清空缓存,不妨碍下面的展开
poolTaskExecutor.execute(() -> {
    // 清理redis缓存
    if ("可视化套餐".equals(addOrPayOrderOutSideReq.getSpan())) {
        subscribeService.deleteVisualToBuyRecord(loginUser);
    }
});


@Override
public void deleteVisualToBuyRecord(LoginUser loginUser) {
    try {
        if (Objects.isNull(LightDataConstant.HS_CACHE.get(LightDataConstant.VISUAL_TO_BUY + loginUser.getAuthId() , Boolean.class))) {
            return;
        }

        // 清理redis缓存
        LightDataConstant.HS_CACHE.evict(LightDataConstant.VISUAL_TO_BUY + loginUser.getAuthId());
    } catch (Exception e) {
        log.error("[subscribe] 可视化权限删除缓存记录 deleteVisualToBuyRecord报错:{}", e.getMessage());
    }
}

自测与思考

  在后续的自测中,会发现,基本能满足权限校验的需求;但思考一个问题:当并发量足够大的时候,在订单提交时开启多线程删除缓存,会不会有时间上的延迟导致用户下订单后立马去访问接口,从而出现权限校验仍然报错的情况?

情景描述: 有两个人同时操作,一个人购买套餐,另一个人立马去请求获取订阅页面,有可能存在订阅前请求调用,然后订阅完以后权限依然没有的情况;如下图所示:
请添加图片描述
  在这种情况主要是因为:当前下单订阅时的并发量过大,在开启多线程清除redis缓存时可能还未排队等到,用户就去调用有图页面了,造成权限获取仍然为false的情况;
  另外,还有一种情况也具有突然性,即虽然恒有数这边下单成功,但开放平台那边用户记录更新不及时,也会造成权限获取仍然为false的情况;

解决方案:可以尝试在本项目与开发平台这边搭建一个消息队列,当下单时将消息放入队列中,当调用权限时去队列中检查有无对应数据即可;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值