最新要闻
- 四部门:深入开展家电以旧换新_天天即时看
- 美国铀期货U3O8价格于6月7日涨至56.25美元
- 方便英文单词_方便英文
- 医疗产业ETF(159877)涨0.67%,开立医疗涨5.08%,华兰股份涨5.31%
- 全球看点:唯美的歌词带意境(唯美的歌词)
- 天天快讯:中国无人驾驶高速地铁海外开工:最大载客量达1100人
- 16核i9+RTX 4060游戏本价格也被国产打下来了 七彩虹将星X15仅售5999|每日聚焦
- 【新要闻】赛力斯董事长张兴海:恶性竞争将毁掉企业和行业
- 全球关注:低头玩手机等于颈椎增压45斤!相当于6岁小孩骑脖子
- 马斯克要横扫欧洲!特斯拉全新超级工厂曝光:投资超300亿 或落地西班牙
- 陆家嘴数智天地·智慧谷项目亮相 金融科技企业拟在此设立研发总部
- 天天滚动:双方家长有矛盾怎么办 双方家长有矛盾怎么解决
- 每日快看:缯怎么读怎么组词_缯怎么读
- 环球时讯:英特尔开始停产11代处理器
- 王治郅视频_王治郅事件-全球最新
- 莫兰迪作品高清大图_莫兰迪作品 每日精选
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
接口防刷处理方案,太优雅了!
来源:juejin.cn/post/7200366809407750181
【资料图】
前言
本文为描述通过Interceptor以及Redis实现接口访问防刷Demo
这里会通过逐步找问题,逐步去完善的形式展示
原理
- 通过ip地址+uri拼接用以作为访问者访问接口区分
- 通过在Interceptor中拦截请求,从Redis中统计用户访问接口次数从而达到接口防刷目的
如下图所示
工程
推荐一个开源免费的 Spring Boot 实战项目:
https://github.com/javastacks/spring-boot-best-practice
其中,Interceptor处代码处理逻辑最为重要
/** * @author: Zero * @time: 2023/2/14 * @description: 接口防刷拦截处理 */@Slf4jpublic class AccessLimintInterceptor implements HandlerInterceptor { @Resource private RedisTemplate redisTemplate; /** * 多长时间内 */ @Value("${interfaceAccess.second}") private Long second = 10L; /** * 访问次数 */ @Value("${interfaceAccess.time}") private Long time = 3L; /** * 禁用时长--单位/秒 */ @Value("${interfaceAccess.lockTime}") private Long lockTime = 60L; /** * 锁住时的key前缀 */ public static final String LOCK_PREFIX = "LOCK"; /** * 统计次数时的key前缀 */ public static final String COUNT_PREFIX = "COUNT"; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); String ip = request.getRemoteAddr(); // 这里忽略代理软件方式访问,默认直接访问,也就是获取得到的就是访问者真实ip地址 String lockKey = LOCK_PREFIX + ip + uri; Object isLock = redisTemplate.opsForValue().get(lockKey); if(Objects.isNull(isLock)){ // 还未被禁用 String countKey = COUNT_PREFIX + ip + uri; Object count = redisTemplate.opsForValue().get(countKey); if(Objects.isNull(count)){ // 首次访问 log.info("首次访问"); redisTemplate.opsForValue().set(countKey,1,second, TimeUnit.SECONDS); }else{ // 此用户前一点时间就访问过该接口 if((Integer)count < time){ // 放行,访问次数 + 1 redisTemplate.opsForValue().increment(countKey); }else{ log.info("{}禁用访问{}",ip, uri); // 禁用 redisTemplate.opsForValue().set(lockKey, 1,lockTime, TimeUnit.SECONDS); // 删除统计 redisTemplate.delete(countKey); throw new CommonException(ResultCode.ACCESS_FREQUENT); } } }else{ // 此用户访问此接口已被禁用 throw new CommonException(ResultCode.ACCESS_FREQUENT); } return true; }}
在多长时间内访问接口多少次,以及禁用的时长,则是通过与配置文件配合动态设置
当处于禁用时直接抛异常则是通过在ControllerAdvice处统一处理 (这里代码写的有点丑陋)
下面是一些测试(可以把项目通过Git还原到“【初始化】”状态进行测试)
- 正常访问时
- 访问次数过于频繁时
自我提问
上述实现就好像就已经达到了我们的接口防刷目的了
但是,还不够
为方便后续描述,项目中新增补充Controller
,如下所示
简单来说就是
PassCotroller
和RefuseController
- 每个Controller分别有对应的get,post,put,delete类型的方法,其映射路径与方法名称一致
接口自由
- 对于上述实现,不知道你们有没有发现一个问题
- 就是现在我们的接口防刷处理,针对是所有的接口(项目案例中我只是写的接口比较少)
- 而在实际开发中,说对于所有的接口都要做防刷处理,感觉上也不太可能(写此文时目前大四,实际工作经验较少,这里不敢肯定)
- 那么问题有了,该如何解决呢?目前来说想到两个解决方案
拦截器映射规则
项目通过Git还原到"【Interceptor设置映射规则实现接口自由】"版本即可得到此案例实现
我们都知道拦截器是可以设置拦截规则的,从而达到拦截处理目的
1.这个AccessInterfaceInterceptor
是专门用来进行防刷处理的,那么实际上我们可以通过设置它的映射规则去匹配需要进行【接口防刷】的接口即可
2.比如说下面的映射配置
3.这样就初步达到了我们的目的,通过映射规则的配置,只针对那些需要进行【接口防刷】的接口才会进行处理
4.至于为啥说是初步呢?下面我就说说目前我想到的使用这种方式进行【接口防刷】的不足点:
所有要进行防刷处理的接口统一都是配置成了 x 秒内 y 次访问次数,禁用时长为 z 秒
- 要知道就是要进行防刷处理的接口,其 x, y, z的值也是并不一定会统一的
- 某些防刷接口处理比较消耗性能的,我就把x, y, z设置的紧一点
- 而某些防刷接口处理相对来说比较快,我就把x, y, z 设置的松一点
- 这没问题吧
- 但是现在呢?x, y, z值全都一致了,这就不行了
- 这就是其中一个不足点
- 当然,其实针对当前这种情况也有解决方案
- 那就是弄多个拦截器
- 每个拦截器的【接口防刷】处理逻辑跟上述一致,并去映射对应要处理的防刷接口
- 唯一不同的就是在每个拦截器内部,去修改对应防刷接口需要的x, y, z值
- 这样就是感觉会比较麻烦
防刷接口映射路径修改后维护问题
- 虽然说防刷接口的映射路径基本上定下来后就不会改变
- 但实际上前后端联调开发项目时,不会有那么严谨的Api文档给我们用(这个在实习中倒是碰到过,公司不是很大,开发起来也就不那么严谨,啥都要自己搞,功能能实现就好)
- 也就是说还是会有那种要修改接口的映射路径需求
- 当防刷接口数量特别多,后面的接手人员就很痛苦了
- 就算是项目是自己从0到1实现的,其实有时候项目开发到后面,自己也会忘记自己前面是如何设计的
- 而使用当前这种方式的话,谁维护谁蛋疼
自定义注解 + 反射
咋说呢
- 就是通过自定义注解中定义 x 秒内 y 次访问次数,禁用时长为 z 秒
- 自定义注解 + 在需要进行防刷处理的各个接口方法上
- 在拦截器中通过反射获取到各个接口中的x, y, z值即可达到我们想要的接口自由目的
下面做个实现
声明自定义注解
Controlller中方法中使用
Interceptor处逻辑修改(最重要是通过反射判断此接口是否需要进行防刷处理,以及获取到x, y, z的值)
/** * @author: Zero * @time: 2023/2/14 * @description: 接口防刷拦截处理 */@Slf4jpublic class AccessLimintInterceptor implements HandlerInterceptor { @Resource private RedisTemplate redisTemplate; /** * 锁住时的key前缀 */ public static final String LOCK_PREFIX = "LOCK"; /** * 统计次数时的key前缀 */ public static final String COUNT_PREFIX = "COUNT"; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 自定义注解 + 反射 实现 // 判断访问的是否是接口方法 if(handler instanceof HandlerMethod){ // 访问的是接口方法,转化为待访问的目标方法对象 HandlerMethod targetMethod = (HandlerMethod) handler; // 取出目标方法中的 AccessLimit 注解 AccessLimit accessLimit = targetMethod.getMethodAnnotation(AccessLimit.class); // 判断此方法接口是否要进行防刷处理(方法上没有对应注解就代表不需要,不需要的话进行放行) if(!Objects.isNull(accessLimit)){ // 需要进行防刷处理,接下来是处理逻辑 String ip = request.getRemoteAddr(); String uri = request.getRequestURI(); String lockKey = LOCK_PREFIX + ip + uri; Object isLock = redisTemplate.opsForValue().get(lockKey); // 判断此ip用户访问此接口是否已经被禁用 if (Objects.isNull(isLock)) { // 还未被禁用 String countKey = COUNT_PREFIX + ip + uri; Object count = redisTemplate.opsForValue().get(countKey); long second = accessLimit.second(); long maxTime = accessLimit.maxTime(); if (Objects.isNull(count)) { // 首次访问 log.info("首次访问"); redisTemplate.opsForValue().set(countKey, 1, second, TimeUnit.SECONDS); } else { // 此用户前一点时间就访问过该接口,且频率没超过设置 if ((Integer) count < maxTime) { redisTemplate.opsForValue().increment(countKey); } else { log.info("{}禁用访问{}", ip, uri); long forbiddenTime = accessLimit.forbiddenTime(); // 禁用 redisTemplate.opsForValue().set(lockKey, 1, forbiddenTime, TimeUnit.SECONDS); // 删除统计--已经禁用了就没必要存在了 redisTemplate.delete(countKey); throw new CommonException(ResultCode.ACCESS_FREQUENT); } } } else { // 此用户访问此接口已被禁用 throw new CommonException(ResultCode.ACCESS_FREQUENT); } } } return true; }}
由于不好演示效果,这里就不贴测试结果图片了
项目通过Git还原到"【自定义主键+反射实现接口自由"版本即可得到此案例实现,后面自己可以针对接口做下测试看看是否如同我所说的那样实现自定义x, y, z 的效果
嗯,现在看起来,可以针对每个要进行防刷处理的接口进行针对性自定义多长时间内的最大访问次数,以及禁用时长,哪个接口需要,就直接+在那个接口方法出即可
感觉还不错的样子,现在网上挺多资料也都是这样实现的
但是还是可以有改善的地方
先举一个例子,以我们的PassController为例,如下是其实现
下图是其映射路径关系
同一个Controller的所有接口方法映射路径的前缀都包含了/pass
我们在类上通过注解@ReqeustMapping
标记映射路径/pass
,这样所有的接口方法前缀都包含了/pass
,并且以致于后面要修改映射路径前缀时只需改这一块地方即可
这也是我们使用SpringMVC最常见的用法
那么,我们的自定义注解也可不可以这样做呢?先无中生有个需求
假设PassController中所有接口都是要进行防刷处理的,并且他们的x, y, z值就一样
如果我们的自定义注解还是只能加载方法上的话,一个一个接口加,那么无疑这是一种很呆的做法
要改的话,其实也很简单,首先是修改自定义注解,让其可以作用在类上
接着就是修改AccessLimitInterceptor
的处理逻辑
AccessLimitInterceptor
中代码修改的有点多,主要逻辑如下
与之前实现比较,不同点在于x, y, z的值要首先尝试在目标类中获取
其次,一旦类中标有此注解,即代表此类下所有接口方法都要进行防刷处理
如果其接口方法同样也标有此注解,根据就近优先原则,以接口方法中的注解标明的值为准
/** * @author: Zero * @time: 2023/2/14 * @description: 接口防刷拦截处理 */@Slf4jpublic class AccessLimintInterceptor implements HandlerInterceptor { @Resource private RedisTemplate redisTemplate; /** * 锁住时的key前缀 */ public static final String LOCK_PREFIX = "LOCK"; /** * 统计次数时的key前缀 */ public static final String COUNT_PREFIX = "COUNT"; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 自定义注解 + 反射 实现, 版本 2.0 if (handler instanceof HandlerMethod) { // 访问的是接口方法,转化为待访问的目标方法对象 HandlerMethod targetMethod = (HandlerMethod) handler; // 获取目标接口方法所在类的注解@AccessLimit AccessLimit targetClassAnnotation = targetMethod.getMethod().getDeclaringClass().getAnnotation(AccessLimit.class); // 特别注意不能采用下面这条语句来获取,因为 Spring 采用的代理方式来代理目标方法 // 也就是说targetMethod.getClass()获得是class org.springframework.web.method.HandlerMethod ,而不知我们真正想要的 Controller// AccessLimit targetClassAnnotation = targetMethod.getClass().getAnnotation(AccessLimit.class); // 定义标记位,标记此类是否加了@AccessLimit注解 boolean isBrushForAllInterface = false; String ip = request.getRemoteAddr(); String uri = request.getRequestURI(); long second = 0L; long maxTime = 0L; long forbiddenTime = 0L; if (!Objects.isNull(targetClassAnnotation)) { log.info("目标接口方法所在类上有@AccessLimit注解"); isBrushForAllInterface = true; second = targetClassAnnotation.second(); maxTime = targetClassAnnotation.maxTime(); forbiddenTime = targetClassAnnotation.forbiddenTime(); } // 取出目标方法中的 AccessLimit 注解 AccessLimit accessLimit = targetMethod.getMethodAnnotation(AccessLimit.class); // 判断此方法接口是否要进行防刷处理 if (!Objects.isNull(accessLimit)) { // 需要进行防刷处理,接下来是处理逻辑 second = accessLimit.second(); maxTime = accessLimit.maxTime(); forbiddenTime = accessLimit.forbiddenTime(); if (isForbindden(second, maxTime, forbiddenTime, ip, uri)) { throw new CommonException(ResultCode.ACCESS_FREQUENT); } } else { // 目标接口方法处无@AccessLimit注解,但还要看看其类上是否加了(类上有加,代表针对此类下所有接口方法都要进行防刷处理) if (isBrushForAllInterface && isForbindden(second, maxTime, forbiddenTime, ip, uri)) { throw new CommonException(ResultCode.ACCESS_FREQUENT); } } } return true; } /** * 判断某用户访问某接口是否已经被禁用/是否需要禁用 * * @param second 多长时间 单位/秒 * @param maxTime 最大访问次数 * @param forbiddenTime 禁用时长 单位/秒 * @param ip 访问者ip地址 * @param uri 访问的uri * @return ture为需要禁用 */ private boolean isForbindden(long second, long maxTime, long forbiddenTime, String ip, String uri) { String lockKey = LOCK_PREFIX + ip + uri; //如果此ip访问此uri被禁用时的存在Redis中的 key Object isLock = redisTemplate.opsForValue().get(lockKey); // 判断此ip用户访问此接口是否已经被禁用 if (Objects.isNull(isLock)) { // 还未被禁用 String countKey = COUNT_PREFIX + ip + uri; Object count = redisTemplate.opsForValue().get(countKey); if (Objects.isNull(count)) { // 首次访问 log.info("首次访问"); redisTemplate.opsForValue().set(countKey, 1, second, TimeUnit.SECONDS); } else { // 此用户前一点时间就访问过该接口,且频率没超过设置 if ((Integer) count < maxTime) { redisTemplate.opsForValue().increment(countKey); } else { log.info("{}禁用访问{}", ip, uri); // 禁用 redisTemplate.opsForValue().set(lockKey, 1, forbiddenTime, TimeUnit.SECONDS); // 删除统计--已经禁用了就没必要存在了 redisTemplate.delete(countKey); return true; } } } else { // 此用户访问此接口已被禁用 return true; } return false; }}
好了,这样就达到我们想要的效果了
项目通过Git还原到"【自定义注解+反射实现接口自由-版本2.0】"版本即可得到此案例实现,自己可以测试万一下
这是目前来说比较理想的做法,至于其他做法,暂时没啥了解到
时间逻辑漏洞
这是我一开始都有留意到的问题
也是一直搞不懂,就是我们现在的所有做法其实感觉都不是严格意义上的x秒内y次访问次数
特别注意这个x秒,它是连续,任意的(代表这个x秒时间片段其实是可以发生在任意一个时间轴上)
我下面尝试表达我的意思,但是我不知道能不能表达清楚
假设我们固定某个接口5秒内只能访问3次,以下面例子为例
底下的小圆圈代表此刻请求访问接口
按照我们之前所有做法的逻辑走
- 第2秒请求到,为首次访问,Redis中统计次数为1(过期时间为5秒)
- 第7秒,此时有两个动作,一是请求到,二是刚刚第二秒Redis存的值现在过期
- 我们先假设这一刻,请求处理完后,Redis存的值才过期
- 按照这样的逻辑走
- 第七秒请求到,Redis存在对应key,且不大于3, 次数+1
- 接着这个key立马过期
- 再继续往后走,第8秒又当做新的一个起始,就不往下说了,反正就是不会出现禁用的情况
按照上述逻辑走,实际上也就是说当出现首次访问时,当做这5秒时间片段的起始
第2秒是,第8秒也是
但是有没有想过,实际上这个5秒时间片段实际上是可以放置在时间轴上任意区域的
上述情况我们是根据请求的到来情况人为的把它放在【2-7】,【8-13】上
而实际上这5秒时间片段是可以放在任意区域的
那么,这样的话,【7-12】也可以放置
而【7-12】这段时间有4次请求,就达到了我们禁用的条件了
是不是感觉怪怪的
想过其他做法,但是好像严格意义上真的做不到我所说的那样(至少目前来说想不到)
之前我们的做法,正常来说也够用,至少说有达到防刷的作用
后面有机会的话再看看,不知道我是不是钻牛角尖了
路径参数问题
假设现在PassController
中有如下接口方法
也就是我们在接口方法中常用的在请求路径中获取参数的套路
但是使用路径参数的话,就会发生问题
那就是同一个ip地址访问此接口时,我携带的参数值不同
按照我们之前那种前缀+ip+uri拼接的形式作为key的话,其实是区分不了的
下图是访问此接口,携带不同参数值时获取的uri状况
这样的话在我们之前拦截器的处理逻辑中,会认为是此ip用户访问的是不同的接口方法,而实际上访问的是同一个接口方法
也就导致了【接口防刷】失效
接下来就是解决它,目前来说有两种
- 不要使用路径参数
这算是比较理想的做法,相当于没这个问题
但有一定局限性,有时候接手别的项目,或者自己根本没这个权限说不能使用路径参数
- 替换uri
- 我们获取uri的目的,其实就是为了区别访问接口
- 而把uri替换成另一种可以区分访问接口方法的标识即可
- 最容易想到的就是通过反射获取到接口方法名称,使用接口方法名称替换成uri即可
- 当然,其实不同的Controller中,其接口方法名称也有可能是相同的
- 实际上可以再获取接口方法所在类类名,使用类名 + 方法名称替换uri即可
- 实际解决方案有很多,看个人需求吧
真实ip获取
在之前的代码中,我们获取代码都是通过request.getRemoteAddr()
获取的
但是后续有了解到,如果说通过代理软件方式访问的话,这样是获取不到来访者的真实ip的
至于如何获取,后续我再研究下http再说,这里先提个醒
总结
说实话,挺有意思的,一开始自己想【接口防刷】的时候,感觉也就是转化成统计下访问次数的问题摆了。后面到网上看别人的写法,又再自己给自己找点问题出来,后面会衍生出来一推东西出来,诸如自定义注解+反射这种实现方式。
以前其实对注解 + 反射其实有点不太懂干嘛用的,而从之前的数据报表导出,再到基本权限控制实现,最后到今天的【接口防刷】一点点来进步去补充自己的知识点,而且,感觉写博客真的是件挺有意义的事情,它会让你去更深入的了解某个点,并且知识是相关联的,探索的过程中会牵扯到其他别的知识点,就像之前的写的【单例模式】实现,一开始就了解到懒汉式,饿汉式
后面深入的话就知道其实会还有序列化/反序列化,反射调用生成实例,对象克隆这几种方式回去破坏单例模式,又是如何解决的,这也是一个进步的点,后续为了保证线程安全问题,牵扯到的synchronized,voliate关键字,继而又关联到JVM,JUC,操作系统的东西。
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!
关键词:
接口防刷处理方案,太优雅了!
Linux磁盘分区扩容-世界今亮点
ChatGTP:没人比我更懂诗歌_当前头条
四部门:深入开展家电以旧换新_天天即时看
美国铀期货U3O8价格于6月7日涨至56.25美元
方便英文单词_方便英文
医疗产业ETF(159877)涨0.67%,开立医疗涨5.08%,华兰股份涨5.31%
全球看点:唯美的歌词带意境(唯美的歌词)
天天快讯:中国无人驾驶高速地铁海外开工:最大载客量达1100人
16核i9+RTX 4060游戏本价格也被国产打下来了 七彩虹将星X15仅售5999|每日聚焦
【新要闻】赛力斯董事长张兴海:恶性竞争将毁掉企业和行业
全球关注:低头玩手机等于颈椎增压45斤!相当于6岁小孩骑脖子
马斯克要横扫欧洲!特斯拉全新超级工厂曝光:投资超300亿 或落地西班牙
陆家嘴数智天地·智慧谷项目亮相 金融科技企业拟在此设立研发总部
天天滚动:双方家长有矛盾怎么办 双方家长有矛盾怎么解决
全球微资讯!websocket
海外直播源码技术文字聊天功能的配置_当前速讯
深入浅出Spring原理及实战「缓存Cache开发系列」
每日快看:缯怎么读怎么组词_缯怎么读
环球时讯:英特尔开始停产11代处理器
王治郅视频_王治郅事件-全球最新
莫兰迪作品高清大图_莫兰迪作品 每日精选
天天消息!22.5万起 小鹏G6开启预售 官方:真正的6边形战士来了
全球实时:惠普推出新款无线耳机:充电盒自带触控屏 控制音乐播放
全球快播:高考考生遇采访喊话:取消调休 取消调休
网购格力空调收到“木各力”牌 商家拒绝退款
余承东:特斯拉FSD进入中国我们也不怕 依然可以遥遥领先
焦点快播:gtx260m显卡(gtx260m)
高考前最后一课丨常德市七中老师收到全班学生送的礼物 笑着笑着就哭了_当前快讯
dnf带幻影的史诗武器(dnf幻影手镯) 世界热闻
当前速递!猪菜磨底VS服务回暖!5月CPI或小幅反弹
热讯:流年
世界百事通!深度学习应用篇-计算机视觉-语义分割综述[5]:FCN、SegNet、Deeplab等分割算法、常用二维三维半立体数据集汇总、前景展望等
每日资讯:美团太细了:Springcloud 微服务优雅停机,如何实现?
全网Jenkins+Gitee+Docker/SSH 部署避坑点总结 每日关注
全球通讯!人工智能(AI)热潮提振了的科技股,并推高了对冲基金的回报,帮助他们挽回去年的损失
今日快讯:热力学第一定律功能关系(热力学第一定律)
热消息:favorite subject(favorite)
天天看点:辽宁省沈阳市2023-06-06 16:27发布大风蓝色预警
每日报道:qq显示iphone在线没有显示4g(qq显示iphone在线)
全球百事通!正整数包括什么分数(正整数包括什么)
鸡汤用高压锅煮要煮多久 高压锅炖鸡汤要压多少分钟
守护者之铠(永恒守护者腿铠)-全球消息
充满“海洋”味!比亚迪宋PLUS冠军版来了:选它还是四驱哈弗枭龙MAX
世界热头条丨精装版丰田陆巡 全新雷克萨斯GX全球首发:能越野的日系豪华!
美国一列满载全新汽车的火车脱轨:弯弯曲曲扭成“贪吃蛇”
快消息!K60系列终极大作!曝Redmi K60 Ultra 7月登场
重庆一女生考完哭着说终于不用做数学了:网友神回复-焦点观察
汉字的演变历史手抄报_汉字的演变历史 天天最新
高考“钉子户”梁实谈第27次高考:文综重大失误
世界短讯!珍惜时间的名人小故事(珍惜时间的名人)
全球播报:百度贴吧怎么注册帐号登录(百度贴吧怎么注册帐号)
热推荐:太极杨氏85赵斌视频(赵斌杨氏85式太极拳)
Nginx安装部署及性能优化 当前看点
每日动态!Map
恩施市教育局电子政务系统(恩施市教育局电子政务登录)
tom ford男友(sufjan stevens男朋友)_头条焦点
全球通讯!“宗”这个字应该如何正确读音?
董明珠:格力空调10年免费包修 对手不敢这么做
天玑9200手机出手 5G上行速度跑出440Mbps新纪录-世界热点
每日看点!扎克伯格批苹果Vision Pro头显:社交属性太差 不如跟Meta
梅西打义乌厂商措手不及:迈阿密新队服来不及生产 每日时讯
北大屠夫称大学生就业难关键在父母:大学生是普通教育 不要把自己当精英 世界播报
今日观点!医保卡_医保卡里的钱可以取出来吗
qq用手机怎么设置空白名字_手机qq怎么弄空白名字
当前速看:【机构调研记录】凯石基金调研江苏雷利
RTOS测试(韩国方案)
pxo理论_pxo-全球动态
runningman超能力特辑第二季_running man超能力特辑|今亮点
泰国(硬盘)
【读财报】年初以来99单IPO项目终止:海通证券数量居首,华金证券撤否率最高
人类首次!我国科研人员监测到伽马射线暴全过程:来自20多亿年前
今热点:iOS 17体验评测:20条Bug 9个变化 不值得升级
每日热门:学生打架老师要求用英语复述过程 两人散装英语让网友笑趴
福建福州:智能服务机器人产销两旺
华电国际:6月8日融券卖出80.13万股,融资融券余额2.83亿元
目标永兴岛 航程900海里 “海巡03”轮首次巡航西沙海域
环球观热点:全能型锋线大将试训勇士,他是双向合同的理性选择?
世界微资讯!半年91家公司涨幅翻倍!林园、谢治宇、赵建平三位大佬谁抓了超级牛股?
【世界快播报】Oil-Dri Corp of America(ODC.US):2023年Q3财报实现营收1.054亿美元
单硫型水化硫铝酸钙英文_单硫型水化硫铝酸钙_世界焦点
江西铜业:业绩说明会定于6月15日举行
天天观焦点:qq三国国战时间表2021_qq三国国战时间
研报存多项问题,招商证券及4名分析师被出具警示函_环球视点
中超联赛有观众使用激光笔向球场照射,中国足协公布处罚决定
商汤:公司B类普通股股份人民币柜台将自6月19日起推出_当前关注
世界新消息丨坚守“全球车标准”,第4代帝豪2023冠军款上市
热头条丨大北农:定增募资不超19.43亿元申请获深交所审核通过
lol全球总决赛s6 lol全球总决赛视频回放)
高中生期末班主任评语大全(高中生期末班主任评语)
高考英语不会的可以选C 苹果:我是Ctrl+C 报道
海航管控空姐体重 超重停飞引热议:这不在监管标准内 其他航空公司不跟进
临港新片区与7家QFLP试点管理企业签署合作备忘录-世界信息
全球微头条丨A股共75只个股发生大宗交易 派特尔溢价率27.65%居首
全球速看:我之前一直在跟新入行的师弟师妹说,趁年轻学习能力强,抓紧时间转行吧!
50道常见高频大厂面试题-微资讯
读改变未来的九大算法笔记07_搜索引擎
安装指定版本的mysql(安装mysql5
A股IPO动态:豪江智能(301320.SZ)等三股今日上市 今日关注
金融工程主要学什么科-全球要闻