惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
T
Threat Research - Cisco Blogs
H
Hacker News: Front Page
N
News and Events Feed by Topic
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
K
Kaspersky official blog
Forbes - Security
Forbes - Security
Webroot Blog
Webroot Blog
Schneier on Security
Schneier on Security
P
Privacy & Cybersecurity Law Blog
H
Heimdal Security Blog
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
V
Vulnerabilities – Threatpost
T
Tenable Blog
T
Tailwind CSS Blog
P
Privacy International News Feed
WordPress大学
WordPress大学
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
博客园 - Franky
Hacker News: Ask HN
Hacker News: Ask HN
Jina AI
Jina AI
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
雷峰网
雷峰网
Vercel News
Vercel News
A
About on SuperTechFans
爱范儿
爱范儿
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
The Last Watchdog
The Last Watchdog
Engineering at Meta
Engineering at Meta
Spread Privacy
Spread Privacy
Security Archives - TechRepublic
Security Archives - TechRepublic
博客园 - 司徒正美
量子位
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Hacker News - Newest:
Hacker News - Newest: "LLM"
Recorded Future
Recorded Future
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Martin Fowler
Martin Fowler
Project Zero
Project Zero

博客园 - 后端AI实验室

我带的那个实习生,比我更依赖AI——但他的问题和我完全不同 我用AI把一个外包需求从30天压到5天交付,然后客户说:下次还找你 我用 AI 设计了一套新人 onboarding 方案,两周后他的产出超出了我的预期 AI编程一年后,我还记得怎么手写代码吗?答案让我沉默了 裁员后我被迫负责运维,用AI从0搭建了可观测性平台 我把代码审查权交给AI两个月,团队发生了什么 等保三级整改,敏感数据加密,数十个系统——3个人用Cursor一周搞定了 我让AI模拟面试官考了我一个小时,然后我沉默了 同一个需求,我先出技术方案,再让AI出方案——差距让我沉默了 3年没人敢碰的老代码,我用AI重构了它——然后翻车了 我让AI review了自己写的代码,然后删掉了30% 我把一个生产Bug的排查过程,交给AI处理——20分钟后我关掉了它 我把同一个需求分别交给初级程序员、高级程序员和AI,结果让我沉默了 我用Cursor开发了3个月,整理出这套提效4倍的工作流 IntelliJ IDEA的统治即将终结?我已经3个月没怎么用它了 公司给团队配了Cursor,然后开始裁员:一个残酷但必须面对的真相 黄仁勋说编程只是打字?一个12年老程序员的冷静思考 springcloud-nacos配置中心 springcloud-nacos服务注册 springcloud-bus消息总线 springcloud-configclient手动刷新
用AI写代码,我差点把漏洞发上线:血泪总结的10个教训
后端AI实验室 · 2026-03-05 · via 博客园 - 后端AI实验室

三周前,我差点在生产环境搞出一个安全事故。

起因很简单:让Cursor写了一个文件上传接口,跑了一下,没报错,直接提交了。

结果被同事review的时候揪出来:

// AI生成的代码
String savePath = uploadDir + fileName; // fileName来自用户输入
File file = new File(savePath);
file.transferFrom(channel);

fileName是用户传进来的,如果传../../etc/passwd,文件就写到服务器根目录去了。

这是一个经典的路径穿越漏洞

功能完全正常,测试全部通过,但任何一个懂安全的人看一眼就知道这是定时炸弹。

那天之后,我把过去3个月用Cursor踩过的坑,全部整理了一遍。

一共10个。每一个都是真实交过的学费。


第一组:认知层的坑

这组坑最普遍。几乎每个刚开始用AI编程工具的人都会踩。

坑1:把Cursor当"更好的补全"在用

我最开始的用法是这样的:

写几个字 → 等AI补全 → 不满意就删 → 重写

用了两周,感觉"也就那样",效率提升有限,还经常被AI的补全打断思路。

后来才明白:这个用法完全用错了方向。

Cursor的核心能力不是补全,是生成

正确的用法是:

描述清楚需求 → AI生成完整代码块 → 我来review和调整

两种用法的效率差距,不是10%,是10倍。

一句话改法: 不要等AI补全,要主动用Ctrl+K描述需求,让AI一次生成完整的函数或模块。


坑2:Prompt写得太模糊,然后怪AI不好用

这是我听到最多的抱怨:

"AI写的代码太烂了,还不如自己写。"

我每次听到这个,都想问一句:你的Prompt写的是什么?

模糊Prompt:

写一个登录接口

AI生成的:

public String login(String username, String password) {
    User user = userMapper.findByUsername(username);
    if (user != null && user.getPassword().equals(password)) {
        return "success";
    }
    return "fail";
}

密码明文比较,没有token,没有异常处理,没有日志。能跑,但没法用。

清楚的Prompt:

写一个用户登录接口,要求:
1. 用户名+密码登录,密码用BCrypt校验
2. 登录失败超过5次,锁定账户30分钟(用Redis存失败次数)
3. 登录成功返回JWT token,有效期7天
4. 记录登录日志:IP、User-Agent、时间、成功/失败
5. 异常统一用BusinessException抛出,返回Result<T>包装
6. 技术栈:Spring Boot 3, MyBatis-Plus, Redis

AI生成的: 80%可以直接用。

AI的质量上限,取决于你Prompt的质量上限。

一句话改法: 写Prompt之前,先想清楚如果让一个新同事来做这个功能,你会告诉他哪些信息。把这些信息全写进去。


坑3:以为AI"懂"你的项目

这个坑很隐蔽。

Cursor确实能读取你的项目文件,但它不知道你们团队的规范、你的代码风格、你的命名习惯。

结果就是:

// 你的项目规范
public class UserService implements IUserService { ... }
// 返回值统一用 Result<T>
// 异常统一用 BusinessException

// AI生成的(没有规范约束时)
public class UserServiceImpl implements UserService { ... }
// 直接返回实体类
// 用 RuntimeException 抛异常

每次生成完都要手动调整,改来改去,效率反而变低了。

解决方案是在项目根目录建一个 .cursorrules 文件:

# 项目规范

## 命名规范
- 接口以I开头:IUserService
- 实现类不加Impl后缀:UserService
- DTO类名以DTO结尾

## 代码规范
- 所有接口返回 Result<T>
- 异常统一用 BusinessException 抛出
- 日志用 @Slf4j,不用 System.out.println
- 常量抽到对应模块的 Constants 类

## 技术栈
- Spring Boot 3.x / JDK 17
- MyBatis-Plus 3.5.x
- Redis(缓存和分布式锁)

配置之后,AI生成的代码直接符合规范,review工作量少了一半。

一句话改法: 第一次用Cursor接入新项目,先花30分钟写好 .cursorrules,后面省的时间是10倍。


第二组:安全和质量的坑

这组坑最危险。踩了可能直接影响线上。

坑4:AI生成的代码能跑,但有安全漏洞

开头的故事就是这个坑。

我把它完整复盘一遍。

AI生成的文件上传接口(问题版本):

@PostMapping("/upload")
public Result<String> upload(@RequestParam MultipartFile file) {
    String fileName = file.getOriginalFilename(); // 直接用用户传入的文件名
    String savePath = uploadDir + fileName;        // 路径直接拼接
    file.transferTo(new File(savePath));
    return Result.success(savePath);
}

三个问题:

  1. getOriginalFilename() 返回的是用户控制的值,可以是 ../../etc/passwd
  2. 没有文件大小限制,可以上传几个G的文件把磁盘撑爆
  3. 没有文件类型校验,可以上传可执行文件

修复之后:

@PostMapping("/upload")
public Result<String> upload(@RequestParam MultipartFile file) {
    // 1. 文件大小校验
    if (file.getSize() > MAX_FILE_SIZE) {
        throw new BusinessException("文件大小超过限制");
    }
    
    // 2. 文件类型校验
    String contentType = file.getContentType();
    if (!ALLOWED_TYPES.contains(contentType)) {
        throw new BusinessException("不支持的文件类型");
    }
    
    // 3. 文件名用UUID重新生成,不用用户传入的
    String ext = FilenameUtils.getExtension(file.getOriginalFilename());
    String safeFileName = UUID.randomUUID() + "." + ext;
    String savePath = uploadDir + safeFileName;
    
    file.transferTo(new File(savePath));
    return Result.success(safeFileName);
}

AI不会主动帮你考虑安全性,除非你在Prompt里明确要求。

一句话改法: 涉及文件操作、用户输入、权限校验的代码,Prompt里加一句"需要考虑安全性,列出可能的安全风险并处理"。


坑5:并发场景AI经常给你挖坑

AI写出来的代码,在单线程下完全没问题。一到并发,就出幺蛾子。

我上个月做消息通知系统时,AI生成了一个重试逻辑:

// AI生成的(有问题)
public void retryFailed() {
    List<NotifyRecord> failedList = notifyRecordMapper.selectFailed();
    for (NotifyRecord record : failedList) {
        if (record.getRetryCount() < 3) {
            doNotify(record);
            record.setRetryCount(record.getRetryCount() + 1);
            notifyRecordMapper.updateById(record);
        }
    }
}

问题:如果这个方法被多个线程同时执行(比如定时任务多实例部署),同一条记录会被重复发送。

修复后:

// 加分布式锁 + 数据库乐观锁
public void retryFailed() {
    List<NotifyRecord> failedList = notifyRecordMapper.selectFailed();
    for (NotifyRecord record : failedList) {
        String lockKey = "notify:retry:" + record.getId();
        // 抢锁,抢不到说明已有其他实例在处理
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 5, TimeUnit.MINUTES);
        if (Boolean.TRUE.equals(locked)) {
            try {
                doNotify(record);
                notifyRecordMapper.incrementRetryCount(record.getId());
            } finally {
                redisTemplate.delete(lockKey);
            }
        }
    }
}

一句话改法: 定时任务、库存扣减、余额变更、状态流转这类场景,Prompt里明确加上"需要考虑并发安全,使用分布式锁或乐观锁"。


坑6:AI不会主动帮你考虑性能

AI的目标是"功能正确",不是"性能最优"。

最常见的是N+1查询问题。

// AI生成的(N+1查询)
public List<OrderVO> getOrders(Long userId) {
    List<Order> orders = orderMapper.selectByUserId(userId);
    return orders.stream().map(order -> {
        OrderVO vo = new OrderVO();
        BeanUtils.copyProperties(order, vo);
        // 每个订单都查一次用户信息
        User user = userMapper.selectById(order.getUserId());
        vo.setUserName(user.getName());
        // 每个订单都查一次商品信息
        Product product = productMapper.selectById(order.getProductId());
        vo.setProductName(product.getName());
        return vo;
    }).collect(Collectors.toList());
}

100个订单 = 201次数据库查询。

一句话改法: 涉及列表查询,Prompt里加"注意避免N+1查询,使用批量查询"。或者review时重点看循环内部有没有数据库调用。


坑7:让AI写测试,但测试是假的

这个坑最难发现。

我让Cursor写单元测试,它生成了一大堆,覆盖率看起来很高。

但仔细看:

@Test
void testLogin_success() {
    // Mock了所有依赖
    when(userMapper.findByUsername("test")).thenReturn(mockUser);
    when(passwordEncoder.matches(any(), any())).thenReturn(true);
    when(jwtUtil.generateToken(any())).thenReturn("mock-token");
    
    Result<String> result = userService.login("test", "123456");
    
    // 只验证了返回值不为空
    assertNotNull(result);
}

测试通过了,但什么都没有真正验证:

  • 没有验证返回的token是不是预期格式
  • 没有验证登录日志是否记录了
  • 没有验证失败次数是否被重置

这种测试,写了等于没写,但覆盖率数字很好看。

一句话改法: 让AI写完测试后,追问一句:"这些测试有没有只验证了mock的返回值,而没有验证真实的业务逻辑?"让AI自己检查。


第三组:习惯层的坑

这组坑最隐蔽,也是从"会用"到"用好"之间最难跨越的鸿沟。

坑8:一次让AI写太多,代码直接失控

刚开始用Cursor的时候,我会这么写Prompt:

帮我实现整个用户模块:注册、登录、修改密码、找回密码、
注销账户、用户信息管理、头像上传、第三方登录

AI生成了一大坨代码,洋洋洒洒800行。

然后我发现:

  • 注册和登录的密码校验逻辑写了两套
  • 日志记录风格不一致,有的用中文有的用英文
  • 异常处理方式三种混用
  • 我根本没办法整体review,只能硬着头皮提交

正确做法:

第一步:帮我实现用户注册功能
[review完,满意了]
第二步:帮我实现用户登录,密码校验复用注册里的逻辑
[review完,满意了]
第三步:继续,修改密码功能

分步骤生成,每步都是可控的,每步都能review,最终质量好得多。

一句话改法: 单次Prompt生成的代码,不要超过100行。超过了就拆。


坑9:看不懂AI的代码,但直接用了

这个习惯很危险。

AI有时候会生成一些你没见过的写法:

// AI生成的
CompletableFuture.allOf(
    CompletableFuture.runAsync(() -> sendEmail(user)),
    CompletableFuture.runAsync(() -> sendSms(user))
).join();

如果你不熟悉CompletableFuture,可能看了一眼觉得"能跑就行"就提交了。

但这里有个问题:.join() 会阻塞当前线程,如果email或者短信服务超时,整个请求就卡死了。正确做法是加超时控制。

你不理解这段代码,你就发现不了这个问题。

我现在遇到看不懂的写法,一定会先问清楚:

你这里用了CompletableFuture.allOf,能解释一下:
1. 这样写的原因是什么?
2. 有没有潜在的风险?
3. 有没有更简单的替代方案?

一句话改法: 设立一条铁律:看不懂的代码,不提交。


坑10:效率上去了,但停止了深度思考

这是最隐蔽的坑,也是我认为最致命的。

用了AI之后,写代码确实快了。但我发现自己开始懒得思考了。

以前设计一个接口,我会想:

  • 这个接口的入参合理吗?
  • 返回值够不够用?
  • 将来扩展会不会有问题?

现在有时候是:

  • 让AI生成一个接口
  • 能跑了
  • 提交

然后两周后,业务需求一变,发现接口设计有根本性的问题,改起来成本极高。

AI提升的是执行速度,但思考这件事,没有任何捷径。

效率高了,省出来的时间,应该用来思考更深的问题:

  • 这个方案是最好的吗?
  • 还有没有更简单的实现?
  • 三个月后再看这段代码,我还能看懂吗?

一句话改法: 每天留出一段"无AI时间",专门用来想架构、想设计、想需求背后的问题。


写在最后

总结一下这10个坑:

认知层:

  1. 把Cursor当补全工具 → 要当成"程序员"用
  2. Prompt太模糊 → 像给新同事交代任务一样写清楚
  3. 没配 .cursorrules → 花30分钟配好,省几十小时

安全和质量: 4. 没考虑安全性 → Prompt里明确要求,代码里重点review 5. 并发场景没处理 → 明确要求加锁,识别并发场景 6. 没考虑性能 → review时重点看N+1和大数据量 7. 测试是假的 → 让AI自我检查测试的有效性

习惯层: 8. 一次生成太多 → 单次不超过100行,分步骤来 9. 看不懂的代码直接用 → 看不懂就问,不提交 10. 停止深度思考 → 保留"无AI时间"


AI让写代码变容易了,但让写代码变得更考验人了。

以前代码写得慢,你有足够的时间思考。

现在代码生成很快,反而需要你更主动地去思考那些AI不会替你想的问题。

这10个坑,我全踩过。

希望你少踩几个。


后端AI实验室 不讲概念,只谈实战 代码开源,每周更新

image