当前位置: 首页 > news >正文

电商项目-秒杀系统(五) 秒杀下单接口限流

一、 秒杀下单接口隐藏

在实际开发中,我们一般都会将后端的访问接口来进行隐藏,从而防止一些恶意用户,去猜测我们的后端地址,来进行恶意的访问。

当前虽然可以确保用户只有在登录的情况下才可以进行秒杀下单,但是无法防止有一些恶意的用户在登录了之后,猜测秒杀下单的接口地址进行恶意刷单。所以需要对秒杀接口地址进行隐藏。

在用户每一次点击抢购的时候,都首先去生成一个随机数并存入redis,接着用户携带着这个随机数去访问秒杀下单,下单接口首先会从redis中获取该随机数进行匹配,如果匹配成功,则进行后续下单操作,如果匹配不成功,则认定为非法访问。

1) 将随机数工具类放入common工程中

public class RandomUtil {
    public static String getRandomString() {
        int length = 15;
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }public static void main(String[] args) {
        String randomString = RandomUtil.getRandomString();
        System.out.println(randomString);
    }
}

2) 秒杀渲染服务定义随机数接口

/**
* 接口加密
* 生成随机数存入redis,10秒有效期
*/
@GetMapping("/getToken")
@ResponseBody
public String getToken(){
    String randomString = RandomUtil.getRandomString();String cookieValue = this.readCookie();
    redisTemplate.boundValueOps("randomcode_"+cookieValue).set(randomString,10, TimeUnit.SECONDS);
    return randomString;
}//读取cookie
private String readCookie(){
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    String cookieValue = CookieUtil.readCookie(request, "uid").get("uid");
    return cookieValue;
}

3) js修改
修改js下单方法

//秒杀下单
add:function(id){
    app.msg ='正在下单';
    //获取随机数
    axios.get("/api/wseckillorder/getToken").then(function (response) {
        var random=response.data;
        axios.get("/api/wseckillorder/add?time="+moment(app.dateMenus[0]).format("YYYYMMDDHH")+"&id="+id+"&random="+random).then(function (response) {
            if (response.data.flag){
                app.msg='抢单成功,即将进入支付!';
            }else{
                app.msg='抢单失败';
            }
        })
    })}

4) 秒杀渲染服务更改
修改秒杀渲染服务下单接口

/**
     * 秒杀下单
     * @param time 当前时间段
     * @param id 秒杀商品id
     * @return
     */
@RequestMapping("/add")
@ResponseBody
public Result add(String time,Long id,String random){//校验密文有效
    String randomcode = (String) redisTemplate.boundValueOps("randomcode").get();
    if (StringUtils.isEmpty(randomcode) || !random.equals(randomcode)){
        return new Result(false, StatusCode.ERROR,"无效访问");
    }Result result = secKillOrderFeign.add(time, id);
    return result;
}

二、 秒杀下单接口限流

因为秒杀的特殊业务场景,生产场景下,还有可能要对秒杀下单接口进行访问流量控制,防止过多的请求进入到后端服务器。对于限流的实现方式,我们之前已经接触过通过nginx限流网关限流。但是他们都是对一个大的服务进行访问限流,如果现在只是要对某一个服务中的接口方法进行限流呢?这里推荐使用google提供的guava工具包中的RateLimiter进行实现,其内部是基于令牌桶算法进行限流计算

1)添加依赖

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>28.0-jre</version>
</dependency>
 

2)自定义限流注解

@Inherited
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {}

3)自定义切面类

@Component
@Scope
@Aspect
public class AccessLimitAop {@Autowired
    private HttpServletResponse httpServletResponse;private RateLimiter rateLimiter = RateLimiter.create(20.0);@Pointcut("@annotation(com.shangcheng.webSecKill.aspect.AccessLimit)")
    public void limit(){}@Around("limit()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){boolean flag = rateLimiter.tryAcquire();
        Object obj = null;try{
            if (flag){
                obj=proceedingJoinPoint.proceed();
            }else{
                String errorMessage = JSON.toJSONString(new Result(false,StatusCode.ERROR,"fail"));
                outMessage(httpServletResponse,errorMessage);
            }
        }catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return obj;}private void outMessage(HttpServletResponse response, String errorMessage) {ServletOutputStream outputStream = null;
        try {
            response.setContentType("application/json;charset=UTF-8");
            outputStream = response.getOutputStream();
            outputStream.write(errorMessage.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }}
}

4)使用自定义限流注解

相关文章:

  • 使用Modelsim手动仿真
  • 题目 3217 ⭐成绩统计⭐【滑动窗口 + 二分搜索】蓝桥杯2024年第十五届省赛
  • 大白话 CSS 中transform属性的常见变换类型(平移、旋转、缩放等)及使用场景
  • 管理 SELinux 安全性
  • 正则表达式详解
  • android13打基础: timepicker控件
  • Vue 3 ref(new Map()) 无法触发watch
  • 力扣35.搜索插入位置-二分查找
  • Linux网络配置(超详细)
  • 「Java EE开发指南」如何用MyEclipse构建一个Web项目?(二)
  • Go权限管理库Casbin和身份验证库jwt-go初试
  • 【2025】Electron + React 架构筑基——从零到一的跨平台开发
  • 电子学会—2024年12月青少年软件编程(图形化)四级等级考试真题——趣味点阵屏
  • 当中国“智算心跳”与全球共振:九章云极DataCanvas首秀MWC 2025
  • 快速掌握EasyOCR应用实战指南
  • Qt常用控件之表格QTableWidget
  • openharmony 软总线-设备发现流程
  • OSPF网络类型:NBMA与P2MP
  • K8s 1.27.1 实战系列(七)Deployment
  • 第八节:基于Winform框架的串口助手小项目---完结优化《C#编程》
  • 海南省城乡建设部网站首页/谷歌搜索引擎入口2023
  • 网站建设管理软件/windows优化大师是自带的吗
  • 西安建设市场信息平台/东莞百度seo新网站快速排名
  • 聊城做wap网站哪儿好/免费制作个人网站
  • 免费建微网站平台/宁波网站推广联系方式
  • 别人在百度冒用公司旗号做网站/太原做网站哪家好