【SpringBoot】28 API接口防刷(Redis + 拦截器)

news2024/11/27 18:09:02

Gitee仓库

https://gitee.com/Lin_DH/system

介绍

常用的 API 安全措施包括:防火墙、验证码、鉴权、IP限制、数据加密、限流、监控、网关等,以确保接口的安全性。

常见措施

1)防火墙
防火墙是网络安全中最基本的安全设备之一,主要用于防止未经授权的网络访问和攻击。
防火墙主要用于过滤和控制网络流量,以保护网络安全。
防火墙可以防止的攻击行为包括:

  • 无效数据包:防火墙可以识别和过滤无效的数据包,如错误的 IP 地址、伪造的数据包、无法识别的协议等。
  • DOS 和 DDOS 攻击:防火墙可以使用不同的技术来检测和阻止 DOS 和 DDOS 攻击,如阻止大量 TCP / UDP 连接、IP 地址过滤、流量限制等。
  • 病毒和蠕虫攻击:防火墙可以使用特定的病毒和蠕虫检测技术,如签名检测、行为检测、模式识别等,来防止这些恶意软件的传播。
  • 网络钓鱼和欺骗攻击:防火墙可以检测、防止网络钓鱼、欺骗攻击,如防止虚假登录页面、欺骗的网站等。
  • 恶意流量攻击:防火墙可以检测和防止恶意流量攻击,如过滤带有恶意载荷的数据包和防止被黑客利用的端口。
  • 网络侦察攻击:防火墙可以使用一些技术来防止网络侦察攻击,如防止扫描、端口扫描、漏洞利用等。

2)验证码
在特定接口上,要求用户在访问前先进行验证码验证,以确保发送该请求的为真实用户。

3)鉴权
要求用户在访问 API 时,进行身份认证,并根据用户的权限进行授权,只允许有权限的用户访问特定的接口。
4)IP限制
仅限特定 IP 范围对 API 的访问,例如允许内网或者加入 IP 白名单的能够访问特定 API 。
5)数据加密
对敏感数据进行加密传输,使用 HTTPS 协议保证数据传输的安全性。
以往很多接口都是使用 HTTP 协议(Hyper Text Transport Protocol,超文本传输协议),用于传输客户端和服务器端的数据。
HTTP 协议使用虽然简单方便,但也存在着问题:

  • 使用明文通讯,传输内容容易被窃听
  • 不验证通讯方的身份,容易遭到伪装
  • 无法证明报文的完整性,报文容易被篡改
    为了解决 HTTP 协议的一系列问题,出现了 HTTPS 协议。HTTPS 协议是在 HTTP 协议上添加了加密机制。
    SSL(Secure Socket Layer,安全套接层)
    TLS(Transport Layer Security,传输层安全)
    HTTPS = HTTP + 加密 + 认证 + 完整性保护
    为了安全性考虑,接口的协议需要使用 HTTPS 协议。

6)限流
设置访问频率限制,例如每分钟、每小时、每天只允许请求访问一定次数,超出限制则返回错误信息或者封禁 IP。
7)监控
监控 API 的访问日志,统计用户对接口的调用情况,对流量激增、某个IP频繁请求同一接口,则自动发送邮件等通知,及时采取相应的安全措施。
8)网关
在 API 和客户端之间引入 API 网关,对请求进行过滤、鉴权、限流等操作,保护后端 API 的安全。

IP限制方式(拦截器)

代码实现

第一步:定义 IP 限制拦截器

IPInterceptor.java

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

/**
 * IP拦截器
 * @author DUHAOLIN
 * @date 2024/11/13
 */
@Component
public class IPInterceptor implements HandlerInterceptor {

    //IP白名单
    private static final List<String> ALLOWED_IPS = Arrays.asList("127.0.0.1", "0:0:0:0:0:0:0:1");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ipAddress = request.getRemoteAddr();

        //不允许访问的IP返回"Access denied"错误信息,并且设置响应的状态码为403(Forbidden)
        if (!ALLOWED_IPS.contains(ipAddress)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("Access denied");
            return false;
        }

        return true;
    }

}

效果图

在这里插入图片描述

限制次数方式(Redis + 拦截器)

Windows安装Redis

Redis 下载链接:https://pan.baidu.com/s/1BMt4cIxjKTtyL3T0_iSC2w 密码:rkne
打开 CMD 命令窗口,在 Redis 安装目录执行如下命令:redis-server.exe redis.windows.conf
在这里插入图片描述

依赖

pom.xml

        <!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- 分布式锁工具 -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>${redission.version}</version>
        </dependency>

配置文件

application.yml

spring:
  redis:
    host: localhost
    port: 6379
    timeout: 10

代码实现

第一步:添加解析 Redis Key 前缀的接口

KeyPrefix.java

package com.lm.system.redis;

/**
 * @author DUHAOLIN
 * @date 2024/11/13
 */
public interface KeyPrefix {

    int expireSeconds();

    String getPrefix();
}

第二步:添加解析 Redis 基础前缀的抽象类

BasePrefix.java

package com.lm.system.redis;

/**
 * @author DUHAOLIN
 * @date 2024/11/13
 */
public abstract class BasePrefix implements KeyPrefix {

    public BasePrefix() {}

    public BasePrefix(String prefix) {
        this(0, prefix);
    }

    private int expireSeconds;
    private String prefix;

    public BasePrefix(int expireSeconds, String prefix) {
        this.expireSeconds = expireSeconds;
        this.prefix = prefix;
    }

    @Override
    public int expireSeconds() {
        return 0; //默认永不过期
    }

    @Override
    public String getPrefix() {
        String simpleName = this.getClass().getSimpleName();
        return simpleName + ":" + prefix;
    }

}

第三步:添加解析用户Key的实现类

AccessKey.java

package com.lm.system.common;

import com.lm.system.redis.BasePrefix;

/**
 * @author DUHAOLIN
 * @date 2024/11/13
 */
public class AccessKey extends BasePrefix {

    public AccessKey() {}

    public AccessKey(String prefix) {
        super(0, prefix);
    }

    public AccessKey(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    public static AccessKey withExpire(int expireSeconds) {
        return new AccessKey(expireSeconds, "prefix");
    }

    @Override
    public int expireSeconds() {
        return super.expireSeconds();
    }

    @Override
    public String getPrefix() {
        return super.getPrefix();
    }

}

第四步:在需要限流的接口上,添加 @AccessLimit 注解。
注:

  • 其他 User 实体类等可以查看 Gitee 仓库(https://gitee.com/Lin_DH/system)。
  • Redis 不能和 cache 缓存一起使用,ServiceImpl中 users 方法使用需要了,需要注释掉 @Cacheable 注解。

UserController.java

    @GetMapping("users")
    @ApiOperation("获取所有用户信息")
    @AccessLimit(seconds = 10, maxCount = 3)
    public String users() {
        List<User> users = userService.queryAllUser();
        return ResultBody
                .build(HttpStatus.OK)
                .setData(users)
                .setCount(users.size())
                .getReturn();
    }

第五步:启动类添加 @EnableCaching 注解

SystemApplication.java

package com.lm.system;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;



@EnableCaching
@SpringBootApplication
@MapperScan("com.lm.system.mapper")
public class SystemApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SystemApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SystemApplication.class);
    }

}

第六步:添加 Redis 操作接口

RedisService.java

package com.lm.system.redis;

import org.redisson.api.RReadWriteLock;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author DUHAOLIN
 * @date 2024/11/13
 */
public interface RedisService {

    /**
     * 保存属性
     */
    void set(String key, Object value, long time);

    /**
     * 保存属性
     */
    void set(String key, Object value);

    /**
     * 获取属性
     */
    Object get(String key);

    /**
     * 删除属性
     */
    Boolean del(String key);

    /**
     * 批量删除属性
     */
    Long del(List<String> keys);

    /**
     * 设置过期时间
     */
    Boolean expire(String key, long time);

    /**
     * 获取过期时间
     */
    Long getExpire(String key);

    /**
     * 判断是否有该属性
     */
    Boolean hasKey(String key);

    /**
     * 按delta递增
     */
    Long incr(String key, long delta);

    /**
     * 按delta递减
     */
    Long decr(String key, long delta);

    /**
     * 获取Hash结构中的属性
     */
    Object hGet(String key, String hashKey);

    /**
     * 向Hash结构中放入一个属性
     */
    Boolean hSet(String key, String hashKey, Object value, long time);

    /**
     * 向Hash结构中放入一个属性
     */
    void hSet(String key, String hashKey, Object value);

    /**
     * 直接获取整个Hash结构
     */
    Map<Object, Object> hGetAll(String key);

    /**
     * 直接设置整个Hash结构
     */
    Boolean hSetAll(String key, Map<String, Object> map, long time);

    /**
     * 直接设置整个Hash结构
     */
    void hSetAll(String key, Map<String, ?> map);

    /**
     * 删除Hash结构中的属性
     */
    void hDel(String key, Object... hashKey);

    /**
     * 判断Hash结构中是否有该属性
     */
    Boolean hHasKey(String key, String hashKey);

    /**
     * Hash结构中属性递增
     */
    Long hIncr(String key, String hashKey, Long delta);

    /**
     * Hash结构中属性递减
     */
    Long hDecr(String key, String hashKey, Long delta);

    /**
     * 获取Set结构
     */
    Set<Object> sMembers(String key);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, Object... values);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, long time, Object... values);

    /**
     * 是否为Set中的属性
     */
    Boolean sIsMember(String key, Object value);

    /**
     * 获取Set结构的长度
     */
    Long sSize(String key);

    /**
     * 删除Set结构中的属性
     */
    Long sRemove(String key, Object... values);

    /**
     * 获取List结构中的属性
     */
    List<Object> lRange(String key, long start, long end);

    /**
     * 获取List结构的长度
     */
    Long lSize(String key);

    /**
     * 根据索引获取List中的属性
     */
    Object lIndex(String key, long index);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value, long time);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Object... values);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Long time, Object... values);

    /**
     * 从List结构中移除属性
     */
    Long lRemove(String key, long count, Object value);

    /**
     * 尝试获取分布式锁
     * @param key
     * @param timeOut
     * @param expireTime
     * @return
     * @throws InterruptedException
     */
    boolean tryLock(String key, long timeOut, long expireTime) throws InterruptedException;

    /**
     * 解锁
     * @param key
     * @return
     */
    void unLock(String key);

    /**
     * 获取分布式读写锁对象
     * @param lockKey
     * @return
     */
    RReadWriteLock getReadWriteLock(String lockKey);
}

第七步:添加 Redis 操作实现类

RedisServiceImpl.java

package com.lm.system.redis;

import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author DUHAOLIN
 * @date 2024/11/13
 */
@Service
public class RedisServiceImpl implements RedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedissonClient redissonClient;


    @Override
    public void set(String key, Object value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    @Override
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean del(String key) {
        return redisTemplate.delete(key);
    }

    @Override
    public Long del(List<String> keys) {
        return redisTemplate.delete(keys);
    }

    @Override
    public Boolean expire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    @Override
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public Long decr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    @Override
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    @Override
    public Boolean hSet(String key, String hashKey, Object value, long time) {
        redisTemplate.opsForHash().put(key, hashKey, value);
        return expire(key, time);
    }

    @Override
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    @Override
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
        return expire(key, time);
    }

    @Override
    public void hSetAll(String key, Map<String, ?> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public void hDel(String key, Object... hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    @Override
    public Boolean hHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }

    @Override
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    @Override
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    @Override
    public Long sAdd(String key, long time, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    @Override
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    @Override
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    @Override
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    @Override
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    @Override
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    @Override
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public Long lPush(String key, Object value, long time) {
        Long index = redisTemplate.opsForList().rightPush(key, value);
        expire(key, time);
        return index;
    }

    @Override
    public Long lPushAll(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    @Override
    public Long lPushAll(String key, Long time, Object... values) {
        Long count = redisTemplate.opsForList().rightPushAll(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Long lRemove(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }

    @Override
    public boolean tryLock(String key, long timeOut, long expireTime) throws InterruptedException {
        RLock lock = redissonClient.getLock(key);
        return lock.tryLock(timeOut, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 解锁
     * @param key
     * @return
     */
    @Override
    public void unLock(String key){
        RLock lock = redissonClient.getLock(key);
        lock.unlock();
    }

    @Override
    public RReadWriteLock getReadWriteLock(String lockKey) {
        return redissonClient.getReadWriteLock(lockKey);
    }
}

第八步:添加访问频率限制拦截器

AntiBrushInterceptor.java

package com.lm.system.interceptor;

import com.lm.system.annotation.AccessLimit;
import com.lm.system.common.AccessKey;
import com.lm.system.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 访问频率限制拦截器
 * @author DUHAOLIN
 * @date 2024/11/13
 */
@Slf4j
public class AntiBrushInterceptor implements HandlerInterceptor {

    private final RedisService redisService;

    public AntiBrushInterceptor(RedisService redisService) {
        this.redisService = redisService;
    }

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断请求是否属于方法请求
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;

            //获取方法中的注解,判断是否有该注解
            AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);
            if (accessLimit == null) {
                return true;
            }

            int seconds = accessLimit.seconds();
            int maxCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();
            String key = request.getRequestURI();

            if (needLogin) {
                //判断是否登录
                key += "_userId001"; //已登录,获取userId
            }

            //从redis中获取用户的访问次数
            AccessKey accessKey = AccessKey.withExpire(seconds);
            String realKey = accessKey.getPrefix() + key;
            Integer count = (Integer) redisService.get(realKey);
            //访问次数处理
            if (count == null) {
                //首次访问
                redisService.set(realKey, 1, 60);
            }
            else if (count < maxCount) {
                //加1
                redisService.incr(realKey, 1);
            }
            else {
                //超出访问次数
                log.info("进入服务降级,时间{}", LocalDateTime.now().format(FORMATTER));
                response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
                response.getWriter().write("Too Many Requests. Number of visits: " + maxCount);
                return false;
            }
        }
        return true;
    }
}

效果图

前三次访问正常,第四次开始返回错误信息。
在这里插入图片描述

项目结构图

在这里插入图片描述

参考链接

如何防范API经常被人频繁调用【https://baijiahao.baidu.com/s?id=1791472081681790682&wfr=spider&for=pc】
API接口防刷的9种方案【https://baijiahao.baidu.com/s?id=1802852256970678261&wfr=spider&for=pc】
Spring Boot 项目的 API 接口防刷【https://www.iocoder.cn/Fight/Spring-Boot-project-API-anti-brush-interface/?self】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2248561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础学安全--shell脚本学习(1)脚本创建执行及变量使用

目录 学习连接 什么是shell shell的分类 查看当前系统支持shell 学习前提 开始学习 第一种执行脚本方法 ​编辑 第二种执行脚本方法 第三种执行脚本方法 变量声明和定义 ​编辑 查看变量 删除变量 学习连接 声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣…

基于FPGA的FM调制(载波频率、频偏、峰值、DAC输出)-带仿真文件-上板验证正确

基于FPGA的FM调制-带仿真文件-上板验证正确 前言一、FM调制储备知识载波频率频偏峰值个人理解 二、代码分析1.模块分析2.波形分析 总结 前言 FM、AM等调制是学习FPGA信号处理一个比较好的小项目&#xff0c;通过学习FM调制过程熟悉信号处理的一个简单流程&#xff0c;进而熟悉…

“AI玩手机”原理揭秘:大模型驱动的移动端GUI智能体

作者&#xff5c;郭源 前言 在后LLM时代&#xff0c;随着大语言模型和多模态大模型技术的日益成熟&#xff0c;AI技术的实际应用及其社会价值愈发受到重视。AI智能体&#xff08;AI Agent&#xff09;技术通过集成行为规划、记忆存储、工具调用等机制&#xff0c;为大模型装上…

路由策略与路由控制实验

AR1、AR2、AR3在互联接口、Loopback0接口上激活OSPF。AR3、AR4属于IS-IS Area 49.0001&#xff0c;这两者都是Level-1路由器&#xff0c;AR3、AR4的系统ID采用0000.0000.000x格式&#xff0c;其中x为设备编号 AR1上存在三个业务网段A、B、C&#xff08;分别用Loopback1、2、3接…

uniapp开发微信小程序笔记8-uniapp使用vant框架

前言&#xff1a;其实用uni-app开发微信小程序的首选不应该是vant&#xff0c;因为vant没有专门给uni-app设置专栏&#xff0c;可以看到目前Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本&#xff0c;并由社区团队维护 React 版本和支付宝小程序版本。 但是vant的优…

Qt-系统相关(1)事件文件

Qt事件 事件介绍 事件是应⽤程序内部或者外部产⽣的事情或者动作的统称。在 Qt 中使⽤⼀个对象来表⽰⼀个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本⾝在不同的时刻发出的。当⽤⼾按下⿏标、敲下键盘&#xff0c;或者是窗⼝需要重新绘制的时候&a…

HarmonyOS4+NEXT星河版入门与项目实战(20)------状态管理@ObjectLink @Observed

文章目录 1、用法图解2、案例实现1、任务类改造2、参数改造变量3、完整代码4、运行效果4、总结1、用法图解 2、案例实现 上一节的案例中,一直有一个功能没有生效,就是任务完成后对应的任务行变灰,任务字体出现中划线删除的效果。而该功能一直不生效的原因就是要改变的数据值…

【小白学机器学习36】关于独立概率,联合概率,交叉概率,交叉概率和,总概率等 概念辨析的例子

目录 1 先说结论 2 联合概率 3 边缘概率 4 (行/列)边缘概率的和 总概率1 5 条件概率 5.1 条件概率的除法公式 5.2 条件概率和联合概率区别 1 先说结论 关于独立概率&#xff0c;联合概率&#xff0c;交叉概率&#xff0c;交叉概率和&#xff0c;总概率 类型含义 …

TCP IP协议和网络安全

传输层的两个协议&#xff1a; 可靠传输 TCP 分段传输 建立对话&#xff08;消耗系统资源&#xff09; 丢失重传netstat -n 不可靠传输 UDP 一个数据包就能表达完整的意思或屏幕广播 应用层协议&#xff08;默认端口&#xff09;&#xff1a; httpTCP80 网页 ftpTCP21验证用户身…

redmi 12c 刷机

刷机历程 一个多月前网购了redmi 12c这款手机, 价格只有550,用来搞机再适合不过了, 拆快递后就开始倒腾,网上有人说需要等7天才能解锁,我绑定了账号过了几天又忍不住倒腾,最后发现这块手机不用等7天解锁成功了,开始我为了获取root权限, 刷入了很火的magisk,但是某一天仍然发现/…

分布式在线评测系统

OnlineJudge 前言所用技术开发环境 1. 需求分析2. 项目宏观结构3. compile_server服务设计3.1 compiler服务设计3.2 runner服务设计3.3 compile_run3.4 compile_server.cpp 4. oj_server服务设计4.1 model设计4.2 view设计4.3 control设计4.3.1 获取题目列表功能4.3.2 获取单个…

政安晨【零基础玩转各类开源AI项目】探索Cursor-AI Coder的应用实例

目录 Cusor的主要特点 Cusor实操 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; Cursor 是 Visual Studio Code 的一个分支。这使我们能够…

第三届航空航天与控制工程国际 (ICoACE 2024)

重要信息 会议官网&#xff1a;www.icoace.com 线下召开&#xff1a;2024年11月29日-12月1日 会议地点&#xff1a;陕西西安理工大学金花校区 &#xff08;西安市金花南路5号&#xff09; 大会简介 2024年第三届航空航天与控制工程国际学术会议&#xff08;ICoACE 2024&a…

Vue前端开发2.3.5 条件渲染指令

本文介绍了Vue中两种条件渲染指令&#xff1a;v-if和v-show。v-if通过布尔值控制元素的DOM树存在&#xff0c;适用于不频繁切换显示状态的场景&#xff1b;v-show则通过CSS的display属性控制显示&#xff0c;适合频繁切换。通过创建单文件组件示例&#xff0c;演示了如何使用这…

初级数据结构——二叉树题库(c++)

这里写目录标题 前言[1.——965. 单值二叉树](https://leetcode.cn/problems/univalued-binary-tree/)[2.——222. 完全二叉树的节点个数](https://leetcode.cn/problems/count-complete-tree-nodes/)[3.——144. 二叉树的前序遍历](https://leetcode.cn/problems/binary-tree-…

Java基础——(一)Java概述

Java特性 简单性&#xff1a;Java与C很相似&#xff0c;但剔除了C中许多比较复杂并且很少使用的功能&#xff0c;比如头文件、指针运算、结构、联合、操作符重载、虚基类等&#xff0c;从而使Java更易于上手、学习。面向对象&#xff1a;Java是一门面向对象语言&#xff0c;具…

打造智能化在线教育平台详解:教培网校APP的架构设计与实现

本篇文章&#xff0c;小编将以教培网校APP的架构设计与实现为核心&#xff0c;深入探讨如何打造一套智能化的在线教育平台&#xff0c;为企业和教育机构提供落地参考。 一、在线教育平台的核心功能需求 构建一个高效的教培网校APP&#xff0c;首先需要明确其核心功能需求。一…

Java学习笔记--继承的介绍,基本使用,成员变量和成员方法访问特点

目录 一&#xff0c;继承 1.什么是继承 2.怎么去继承: 3.注意: 4.继承怎么学 二&#xff0c;继承基本使用 三&#xff0c;成员变量和成员方法访问特点 1.成员变量访问特点 1&#xff0c;子类和父类中的成员变量不重名: 总结: 2&#xff0c;子类和父类中的成员变量重…

IDEA2024创建一个spingboot项目

以下是创建一个基本的 Spring Boot 项目的步骤和示例&#xff1a; 初始化一个springboot工程其实有许多方法&#xff0c;笔者这里挑了一个最快捷的方式搭建一个项目。我们直接通过官方平台&#xff08;start.spring.io&#xff09;进行配置&#xff0c;然后下载压缩包就可以获取…

【Leetcode】3206.交替组1

题目描述&#xff1a; https://leetcode.cn/problems/alternating-groups-i/description/?envTypedaily-question&envId2024-11-26 题目示例&#xff1a; 解题思路 思路一&#xff1a; 1.如果color.size()小于等于2&#xff0c;则构不成环&#xff0c;直接返回结果…