面试通关:JWT 认证与双 Token 机制深度解析

作者:Lee川日期:2026/5/9

面试通关:JWT 认证与双 Token 机制深度解析

本文专为面试准备,以问答形式拆解 JWT 认证体系的核心考点。每个问题都包含"面试怎么说"和"代码怎么写的"两个层面,让你既能侃侃而谈,也能落笔有物。


开篇:为什么面试官爱问 JWT?

在前后端分离成为主流的今天,身份认证 是每个系统必须解决的第一道门槛。JWT(JSON Web Token)作为无状态认证方案的代表,几乎出现在每一份后端/全栈岗位的 JD 中。面试官问 JWT,通常是在考察三个层面的理解:

层面考察点典型问题
原理层你真的懂 JWT 是什么吗?"JWT 的结构是怎样的?签名有什么用?"
架构层你知道为什么这么设计吗?"为什么需要双 Token?为什么不直接用 Session?"
工程层你踩过坑吗?会怎么解决?"Token 过期了怎么办?Token 被窃取了怎么处理?"

下面我们逐层深入。


第一章 JWT 的本质:不只是"一串加密字符串"

1.1 面试官问:"能说说 JWT 是什么吗?"

不要这么答(减分回答):

"JWT 就是一个加密的 token,用来做用户认证的。"

这个回答有三个问题:JWT 不是加密的(它是签名而非加密)、混淆了认证和授权的概念、没有展示任何深度理解。

可以这样答(加分回答):

"JWT 全称是 JSON Web Token,它是一种基于 JSON 的开放标准(RFC 7519),用于在各方之间安全地传输信息。核心特点是——JWT 是签名的,不是加密的。也就是说,任何人都能解码看到里面的内容,但没人能篡改它,因为篡改后签名就对不上了。

在认证场景中,JWT 解决的核心问题是:在无状态的分布式系统中,服务端如何在不查数据库的情况下确认'你就是你'。"

1.2 面试官追问:"JWT 由哪几部分组成?"

JWT 的结构用一句话记住:三段 Base64,用点分隔

1eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2                                                                                                       
3          Header(头部)                                      Payload(负载)                                        Signature(签名)
4

逐段拆解:

Header — 描述 Token 的元信息

1{
2  "alg": "HS256",    // 签名算法:HMAC-SHA256
3  "typ": "JWT"       // 令牌类型
4}
5

用 base64 编码后就变成了第一段:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload — 要传递的数据

1{
2  "sub": "1",          // subject:用户唯一标识(JWT 标准字段)
3  "name": "zhangsan",  // 自定义字段:用户名
4  "iat": 1715000000,   // issued at:签发时间(JWT 标准字段)
5  "exp": 1715000900    // expiration:过期时间(JWT 标准字段)
6}
7

Signature — 防篡改的核心

1HMACSHA256(
2    base64UrlEncode(header) + "." + base64UrlEncode(payload),
3    secret
4)
5

签名计算的本质是:将 header 和 payload 拼接后,用密钥做一次 HMAC-SHA256 哈希。任何人修改了 header 或 payload 中的哪怕一个字符,生成的签名就会完全不一样——而攻击者没有密钥,无法重新生成有效签名。

1.3 项目中怎么实现的?

在我们的 NestJS 后端中,Token 签发逻辑在 AuthService.generateTokens() 中:

1// auth.service.ts
2private async generateTokens(id: string, name: string) {
3    const payload = { sub: id, name };   // sub  JWT 规范中的 subject 字段
4
5    const [access_token, refresh_token] = await Promise.all([
6        this.jwtService.signAsync(payload, { expiresIn: '15m' }),
7        this.jwtService.signAsync(payload, { expiresIn: '7d'  }),
8    ]);
9
10    return { access_token, refresh_token };
11}
12

值得注意的设计选择:

  • sub 字段:遵循了 JWT 规范,使用 sub(subject/主体)存储用户 ID,而非自定义字段名如 userId。这在多系统对接时更容易被理解。
  • Promise.all 并发签发:两个 Token 的签发没有依赖关系,并行执行减少等待时间。
  • 同一个 payload:两个 Token 携带的信息完全相同,唯一的区别是 exp(过期时间)。这个设计保证了用 refresh_token 换取新的 access_token 时,无需重新查询数据库。

第二章 JWT vs Session:经典对决

2.1 面试官问:"JWT 和 Session 的区别是什么?什么时候用哪个?"

这是 JWT 面试的"必考题"。面试官想听的不是"JWT 比较新所以用 JWT",而是你对两种方案的取舍理解

核心差异:状态存在哪里

1Session 方案                          JWT 方案
2───────────────                      ─────────
3状态存在服务端                          状态存在客户端
4┌──────────┐                        ┌──────────┐
5  浏览器                              浏览器    
6  Session ID│                         完整的    
7  = "abc"                            JWT Token│
8└─────┬─────┘                        └─────┬─────┘
9                                          
10      携带 Session ID                       携带完整 JWT
11                                          
12                                          
13┌──────────────┐                     ┌──────────────┐
14   服务端                               服务端       
15                                                 
16  Session                           无需查询任何表 
17  "abc"  {                          直接用密钥验签 
18    userId:1,                        即可确认身份   
19    name:"zs"                                    
20  }                                              
21└──────────────┘                     └──────────────┘
22   有状态(Stateful)                     无状态(Stateless)
23

一句话总结:Session 是"服务端记账",JWT 是"服务端看过你身份证就行"。

完整对比表

维度SessionJWT
存储位置服务端(内存/Redis/DB)客户端(浏览器)
扩展性需要共享 Session 存储(Redis 集群)天然支持水平扩展
注销控制即时(删除服务端记录即可)难以即时失效(需配合黑名单)
CSRF 风险存在(Cookie 自动携带)较低(需手动附加 Header)
XSS 风险HttpOnly Cookie 可防御localStorage 易被 XSS 读取
每次请求开销查一次 Redis/DB做一次 HMAC 验签(CPU 运算)
多设备登录难以天然支持每设备独立签发 Token
适合场景单体应用、强实时注销需求微服务、移动端、跨域 API

选择建议(面试脱口而出版)

"选 Session 还是 JWT,核心看两个维度:一是系统是否分布式,二是对即时注销的需求有多强。

如果是一个单体的企业内部系统,需要管理员能随时踢人下线,Session 更好——服务端删一条 Redis 记录就完事了。

如果是微服务架构、同时有 Web 端和移动端,JWT 更合适——不需要所有服务都依赖同一个 Redis 来做 Session 校验,每个服务拿到 JWT 自己验签就行。"

2.2 面试官追问:"JWT 最大的缺点是什么?你怎么解决的?"

这个问题也是高频题。JWT 最大的痛点是 Token 无法主动失效

"JWT 一旦签发,在过期之前它就是有效的。Session 方案中,管理员可以在服务端直接删除 Session,用户立刻就被踢出去了。但 JWT 做不到——因为状态在客户端,除非你引入额外的机制。"

三种常见的补救方案:

方案实现方式代价
黑名单Redis 中维护一个已失效 Token 列表每次请求多一次 Redis 查询,部分丧失无状态优势
短有效期 + 刷新access_token 设 15 分钟,用 refresh_token 续期不能做到"即时"失效,最多等 15 分钟
版本号用户表中加一个 tokenVersion 字段,JWT 中携带版本号需要查数据库,依然丧失无状态优势

我们的项目选择了方案二——短有效期 + 刷新机制,同时这也是引出双 Token 机制的绝佳过渡。


第三章 双 Token 机制:面试中的进阶考点

3.1 面试官问:"你们的系统为什么设计两个 Token?"

如果前面的 JWT 基础是 60 分的及格线,双 Token 机制就是拉开分差的关键题。

面试话术

"这是一个安全性和用户体验的经典权衡。核心思路是:用两个不同生命周期的 Token,分别服务于两个不同的使用场景

access_token 是'工作证',只有 15 分钟有效期。它高频使用——挂在每个 API 请求的 Authorization 头里。十几分钟的有效期意味着即使它在传输过程中被中间人截获,攻击窗口也只有 15 分钟。

refresh_token 是'身份证',有效期 7 天。它低频使用——只在 access_token 过期后才拿出来用一次,换取新的一对 Token。因为传输频率低,暴露的风险就小。

如果只有一个长期 Token,一旦被窃取,攻击者可以冒充用户整整 7 天。如果只有一个短期 Token,用户每 15 分钟就要重新输入密码。双 Token 机制在这两个极端之间找到了平衡点。"

3.2 面试官追问:"具体怎么实现双 Token 的签发和刷新?"

签发(Login 时)

1// auth.service.ts  login 方法
2async login(loginDto: LoginDto) {
3    // 1. 查用户
4    const user = await this.prisma.user.findUnique({
5        where: { name: loginDto.name }
6    });
7
8    // 2. 验密码
9    if (!user || !(await bcrypt.compare(loginDto.password, user.password))) {
10        throw new UnauthorizedException('用户名或密码错误');
11    }
12
13    // 3. 签发双 Token:用 Promise.all 并行生成,不浪费时间
14    return {
15        ...(await this.generateTokens(String(user.id), user.name)),
16        user: { id: String(user.id), name: user.name },
17    };
18}
19
20// 核心:两个 Token,共享 payload,不同的过期时间
21private async generateTokens(id: string, name: string) {
22    const payload = { sub: id, name };
23    return {
24        access_token:  await this.jwtService.signAsync(payload, { expiresIn: '15m' }),
25        refresh_token: await this.jwtService.signAsync(payload, { expiresIn: '7d' }),
26    };
27}
28

刷新(access_token 过期后)

1// auth.service.ts  refreshToken 方法
2async refreshToken(rt: string) {
3    try {
4        // 验证 refresh_token 是否仍然有效
5        const payload = await this.jwtService.verifyAsync(rt, {
6            secret: process.env.TOKEN_SECRET,
7        });
8        // 有效  签发全新的一对 Token(滚动更新)
9        return this.generateTokens(payload.sub, payload.name);
10    } catch (e) {
11        throw new UnauthorizedException('Refresh Token 已失效,请重新登录');
12    }
13}
14

滚动更新的巧妙之处:注意 refreshToken() 不是只返回新的 access_token,而是调用 generateTokens() 返回全新的一对 Token。这意味着每次刷新:

  • 旧的 access_token 被替换 ✓
  • 旧的 refresh_token 也被替换 ✓

这带来的安全收益是:如果攻击者窃取了某个 refresh_token,一旦合法用户正常使用并触发了一次刷新,攻击者手中的 refresh_token 就变成了废纸。

3.3 前端如何配合双 Token 机制?

存储:Token 通过 Zustand + persist 中间件持久化到 localStorage。

1// useUserStore.ts
2const useUserStore = create<UserState>()(
3    persist(
4        (set) => ({
5            accessToken: null,
6            refreshToken: null,
7            user: null,
8            isLogin: false,
9
10            login: async (credentials) => {
11                const res = await doLogin(credentials);    // POST /api/auth/login
12                set({
13                    accessToken: res.access_token,
14                    refreshToken: res.refresh_token,
15                    user: res.user,
16                    isLogin: true,
17                });
18            },
19        }),
20        {
21            name: 'user-store',       // localStorage key
22            partialize: (state) => ({  // 只持久化这些,login 方法不会被序列化
23                accessToken: state.accessToken,
24                refreshToken: state.refreshToken,
25                user: state.user,
26                isLogin: state.isLogin,
27            }),
28        }
29    )
30);
31

自动携带:Axios 请求拦截器从 Store 中读取 accessToken,注入到每个请求的 Authorization 头。

1// api/config.ts
2axios.interceptors.request.use(config => {
3    const token = useUserStore.getState().accessToken;  //  getState() 而非 hook
4    if (token) {
5        config.headers.Authorization = `Bearer ${token}`;
6    }
7    return config;
8});
9

面试加分点:解释为什么用 getState() 而非 useUserStore() —— 拦截器是在 React 组件树之外执行的,不能用 hook,而 getState() 是 Zustand 提供的命令式 API,可以在任何地方读取 Store 的当前值。

3.4 面试官可能会追问:"为什么不把 Token 存在 Cookie 里?"

这又是一个展示深度理解的好机会。

存储方式优点缺点适合场景
localStorageAPI 简单,不随请求自动发送XSS 攻击可直接读取SPA + 手动管理 Header
HttpOnly CookieJS 无法读取,防 XSS自动随请求发送(CSRF 风险)服务端渲染、同域应用
内存变量绝对安全刷新页面即丢失高安全场景、配合 refresh_token

"选 localStorage 还是 Cookie,核心区别在于两个安全风险的取舍:localStorage 怕 XSS,Cookie 怕 CSRF。本项目选 localStorage 是因为作为 SPA 应用,Token 需要由 JS 主动管理并拼接到 Authorization Header 中,而 HttpOnly Cookie 对 JS 是不可见的。安全性的补强是通过**缩短 access_token 的有效期(15 分钟)**来降低 XSS 泄漏后的风险窗口。"


第四章 AuthGuard:路由守卫的两层防线

4.1 面试官问:"后端怎么保护需要认证的接口?"

话术

"后端用的是 NestJS 的 Guard 机制,具体实现是一个叫 JwtAuthGuard 的守卫类。它本质上是一个中间的拦截层——在请求到达 Controller 之前把它拦下来,验证身份。如果没带 Token 或者 Token 无效,直接返回 401,Controller 根本不会执行。"

源码拆解

1// guard/jwt-auth.guard.ts  守卫类本身(一句话实现)
2@Injectable()
3export class JwtAuthGuard extends AuthGuard('jwt') {}
4

就一行代码?没错。真正的验证逻辑在 JwtStrategy 中:

1// jwt.strategy.ts  Passport 策略,定义了"怎么验证"
2@Injectable()
3export class JwtStrategy extends PassportStrategy(Strategy) {
4    constructor() {
5        super({
6            //  Token 从哪里来:从 Authorization: Bearer <token> 中提取
7            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
8            //  过期 Token 怎么处理:直接拒绝,绝不放行
9            ignoreExpiration: false,
10            //  用什么密钥验签:和签发时用的同一个 secret
11            secretOrKey: process.env.TOKEN_SECRET || ""
12        });
13    }
14
15    //  验证通过后,从 payload 中提取什么信息放到 request.user
16    async validate(payload: any) {
17        return {
18            id: payload.sub,   // sub  id 的字段映射
19            name: payload.name,
20        };
21    }
22}
23

执行流程可视化

1请求进入
2    
3    
4┌─ JwtAuthGuard ──────────────────────────────────────┐
5     Authorization header 中提取 Bearer token       
6    检查 token 是否存在                                
7      不存在  401 Unauthorized                        
8      存在    交给 JwtStrategy                         
9└────────────────────┬────────────────────────────────┘
10                     
11                     
12┌─ JwtStrategy ───────────────────────────────────────┐
13     TOKEN_SECRET 验证签名                           
14      签名不匹配  401 Unauthorized(token 被篡改过)     
15      签名匹配    继续                                  
16    检查是否过期(ignoreExpiration: false)             
17      已过期  401 Unauthorized                        
18      未过期  继续                                     
19    调用 validate(payload)                            
20      return { id: payload.sub, name: payload.name }  
21       挂载到 request.user                            
22└────────────────────┬────────────────────────────────┘
23                     
24                     
25              Controller 方法
26         req.user = { id: "1", name: "zhangsan" }
27

4.2 面试官问:"前端也有路由守卫,和后端的有什么区别?"

这是一个考察安全意识的问题。

后端守卫(真正安全的):

1// 装饰器声明:这个接口需要认证
2@Get('mine')
3@UseGuards(JwtAuthGuard)
4async getMyPosts(@Req() req) {
5    // 在这里 req.user 一定是真实有效的
6    // 因为这个方法能被调用的唯一前提是 JwtAuthGuard 放行了
7    return this.postsService.findByUser(req.user.id);
8}
9

前端守卫(提升体验的):

1// App.tsx  体验层面的守卫
2const needsLoginPath = ['/mine', '/order', '/chat'];
3
4useEffect(() => {
5    if (!isLogin && needsLoginPath.includes(pathname)) {
6        navigate('/login');  // 没登录就走你!
7    }
8}, [isLogin, pathname]);
9

关键论述

"前端守卫和后端守卫解决的是两个完全不同的问题。前端守卫是用户体验问题——防止用户看到一个全是报错的空白页面。后端守卫是安全问题——防止未授权的请求真的访问到数据。

一个常见的误区是:'前端已经做了路由守卫,后端可以放松一点。'这是大错特错的。因为前端代码运行在用户的浏览器中,我可以打开 DevTools 把 isLogin 改成 true,可以用 curl 直接发包绕过整个前端。前端守卫本质上是一扇没锁的门——它引导好人走正门,但挡不住坏人翻墙。

真正的安全防线永远是后端的 JwtAuthGuard,它不信任任何客户端数据,只信任自己验签的结果。"

4.3 面试官追问:"JwtAuthGuard 是怎么注册到 NestJS 的模块系统中的?"

考察对 NestJS 依赖注入的理解:

1// auth.module.ts
2@Module({
3    imports: [
4        JwtModule.register({
5            secret: process.env.TOKEN_SECRET          // 全局注册 JWT 模块和密钥
6        })
7    ],
8    controllers: [AuthController],                    // 登录、刷新接口
9    providers: [AuthService, JwtStrategy, JwtAuthGuard], // 守卫注册为 Provider
10    exports: [JwtAuthGuard]                           // 导出给其他模块使用
11})
12export class AuthModule {}
13

"JwtAuthGuard 被注册为 AuthModule 的 provider 并被导出。其他模块(比如 PostsModule)只需要在 imports 中引入 AuthModule,就可以在 Controller 上通过 @UseGuards(JwtAuthGuard) 直接使用。这是 NestJS 模块系统的一个设计优势——守卫作为服务可以被注入和共享。"


第五章 完整面试问答模拟

第一回合:基础认知

Q: "你们的系统怎么做用户认证的?"

"用的是基于 JWT 的无状态认证方案。用户登录成功后,后端签发两个 Token——一个 15 分钟有效的 access_token 用于日常请求鉴权,一个 7 天有效的 refresh_token 用于过期后续期。前端拿到后用 Zustand 管理状态并持久化到 localStorage,每次发请求时通过 Axios 拦截器自动携带 Bearer Token。后端通过 NestJS 的 JwtAuthGuard 守卫保护需要认证的接口,守卫内部用 Passport 的 JWT 策略做签名验证和过期检查。"

第二回合:双 Token 深度

Q: "为什么要用两个 Token?一个长有效期的 Token 不行吗?"

"纯粹从功能上讲,一个长期 Token 也能跑。但安全性上会有严重问题:这个 Token 会被高频传输在网络上,一旦被中间人截获,攻击者就拥有了长达 N 天的冒充权限。

双 Token 的本质思路是通过频率隔离来降低风险。access_token 高频使用但短期有效——即使泄露,15 分钟后自动失效。refresh_token 长期有效但低频使用——大部分时间躺在 localStorage 里,很少在网络中传输,暴露概率大幅降低。

这就像你不会把身份证天天挂在脖子上出门——日常用门禁卡就够了,门禁卡丢了损失有限;身份证只在必要时才拿出来。"

第三回合:守卫机制

Q: "AuthGuard 具体是怎么工作的?"

"NestJS 的 AuthGuard 是基于 Passport.js 的封装。它做的事情本质上是一个验证链

第一步,从请求的 Authorization 头中提取 Bearer Token 字符串。 第二步,用 TOKEN_SECRET 密钥验证 JWT 签名,确保 Token 没有被篡改。 第三步,检查 Token 的 exp 字段,判断是否过期。 第四步,调用 validate() 方法,将 JWT payload 中的应用字段(如 sub)映射为业务中更好理解的对象(如 request.user.id),然后把它挂载到 request 对象上。

任何一步失败,整个请求直接 401,Controller 里的业务逻辑根本不会执行。"

第四回合:安全攻防

Q: "如果有人窃取了 access_token,你怎么防御?"

"分三层来答:

第一层是事前预防:access_token 只有 15 分钟有效期,把攻击窗口压缩到最小。这是最核心的防线。

第二层是事中检测:可以通过 IP 变更、设备指纹等异常检测来感知 token 可能被盗用。不过这需要额外的基础设施。

第三层是事后止损:如果确实发现 token 泄露,目前可以通过让用户修改密码并配合 token 版本号机制来批量失效所有已签发的 token。

但也要坦诚地说——JWT 无状态的天性决定了它无法做到像 Session 那样即时撤销。这是选择 JWT 方案时必须接受的 trade-off。如果系统对'即时踢人下线'有硬性要求,应该考虑 Session 方案或为 JWT 补充黑名单机制。"

第五回合:实战踩坑

Q: "做这个认证系统时遇到过什么问题?"

"说一个比较典型的——Token 过期后的静默处理

用户正在填一个表单,15 分钟过去了,access_token 悄悄过期了。当他点击提交时,后端返回 401,前端直接抛错,用户一脸茫然。

理想的处理方案是在 Axios 的响应拦截器中捕获 401 错误,自动用 refresh_token 去换新的 access_token,然后透明地重试原来的请求。用户全程无感知。

这看上去简单,但实际实现时有几个细节需要注意:一是并发请求时会同时触发多个刷新,需要给刷新操作加锁,确保只发一次刷新请求;二是要考虑刷新失败的降级策略——可能是 refresh_token 也过期了,这时就该引导用户重新登录。"


第六章 面试加分要点速记表

主题关键词一句话要点
JWT 本质签名 ≠ 加密JWT 的内容可被任何人解码查看,但签名保证了不可篡改
JWT 结构Header.Payload.Signature三段 Base64,点分隔,签名 = HMAC(header.payload, secret)
无状态服务端不存状态每次验签即认证,无需查数据库
双 Token频率隔离高频短效 + 低频长效 = 安全与体验的平衡
15 分钟攻击窗口access_token 过期越快,被窃取后的损失越小
滚动刷新refresh 也换新每次刷新同时颁发新 access_token 和新 refresh_token
AuthGuard验证链提取 Token → 验签 → 查过期 → 注入 req.user
前后端守卫体验 ≠ 安全前端守卫治未病(引导体验),后端守卫治已病(真正安全)
XSS vs CSRF存储选择localStorage 怕 XSS,HttpOnly Cookie 怕 CSRF,选哪个看你能防哪个
即时失效JWT 的死穴签发即有效,无法服务端单方面撤销,需黑名单或版本号补充

结语

JWT 认证之所以成为面试中的高频话题,不是因为它有多复杂,而是因为它踩中了分布式系统身份认证中最核心的几个矛盾:安全与便捷、有状态与无状态、即时撤销与自主校验。一套成熟的双 Token + AuthGuard 方案,本质上是对这些矛盾的务实的折中。

面试时如果能把"为什么是 15 分钟而不是 10 分钟或 30 分钟"、"为什么 refresh_token 也要滚动更新"、"为什么前端守卫不能替代后端守卫"这几个问题讲清楚,你就已经不是在背答案了,而是在展示你对系统设计的真实思考。


面试通关:JWT 认证与双 Token 机制深度解析》 是转载文章,点击查看原文


相关推荐


【动态规划算法】(斐波那契数列模型详解)
承渊政道2026/4/29

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》 ✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介: 在算法学习的过程中,动态规划始终是一个绕不开的重要主题.它不仅是解决复杂问题的高效工具,也是面试和竞赛中出现频率极高的核心考点.对于初学者而言,动态规划之所以难,往往不在于代码实现本身,而在于如何理解


从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代
常利兵2026/4/21

从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代 引言:实时通信的需求与挑战 在当今数字化时代,互联网应用的实时交互需求日益增长。从在线聊天、股票行情实时更新,到多人协作办公、在线游戏等场景,实时通信已成为提升用户体验和业务效率的关键因素。传统的 HTTP 协议基于请求 - 响应模式,客户端发起请求,服务器被动响应,这种模式在实时通信场景中存在诸多局限性,如高延迟、高开销以及单向性(服务器无法主动推送数据,需客户端轮询) 。为了满足实时通信的需求,WebSo


公网 IP、私网 IP、路由表、转发表与 MAC 地址的关系
小红的布丁2026/4/12

引言 学习网络时,最容易混淆的不是协议流程,而是几个看起来相近、其实不在一个层面的概念,比如: 私网 IP 和公网 IP路由表和转发表“在链路上”到底是什么意思MAC 地址和 IP 地址分别属于哪一层 这篇文章把这些概念放到同一条线上梳理清楚,尽量用能直接形成画面的方式去理解。 私网 IP、公网 IP 和 NAT 到底是什么关系 很多人第一次接触家庭网络时,容易把 192.168.x.x 这类地址叫成“虚拟 IP”。这个说法不够准确,更准确的叫法应该是: 公网 IP私网 IPNAT 什么是公网


大模型推理凭什么这么贵?从GRPO到BCR,推理效率之战全解析
陆业聪2026/4/4

一个反常识的观察:推理越强,账单越贵 DeepSeek-R1 横空出世之后,"o1-style 推理"几乎成了大模型进化的标配动作。CoT、长思考链、自我反思……这些机制确实让模型在数学、代码、逻辑推理上表现亮眼。但随之而来的问题是——每一次"深度思考",都是在烧钱。 最极端的案例:让 o1 解一道简单的小学数学题,它会生成好几百个 token 的"内心独白",然后给出一个一眼就能看出来的答案。这就像雇了个博士级顾问,让他帮你决定午饭吃什么——能力没问题,但成本不对等。 这个问题的本质不是"推理


SwiftUI 如何实现 Infinite Scroll?
RickeyBoy2026/3/27

欢迎点个 star:github.com/RickeyBoy/R… 面试题:用 SwiftUI 实现一个无限滚动列表,支持分页加载。 这道题我在面试中遇到过好几次,说实话第一次答的时候以为随便写个 LazyVStack + onAppear 就完事了。后来才发现,面试官真正想考的不是你会不会用 API,而是你对状态管理、性能优化、Task 生命周期这些东西到底理解多深。 我的思路是从最简方案出发,一步步暴露问题、一步步优化。在开始写代码之前,先聊一下架构选型。 为什么选 MVVM? 先说一下


基于 Cloudflare 生态的 AI Agent 实现
Surmon2026/3/19

2026 新年的一个夜晚,窗外炮竹烟花争相闪耀,脑海里灵光一闪:我这快十年的老博客能不能也赶一波时髦,实现一个真正「有用」的智能助手? 有用 的意思是,它不能是一个只会随便聊天的机器人,而是一个真正了解我(博主)、了解博客内容的 AI 分身。它最好能事无巨细地知道我写过哪些文章,了解我的观点、立场和经历,能根据访客的问题去知识库里精准地找到最相关的内容,再结合上下文给出自然又富有意义的回答。 它应该是一张鲜活、灵动的个人名片。 这并不是一个多么复杂的需求,开源工具和商业基建也已经很成熟了,但真正


从零开发一个掘金自动发布 Skill,并上架 Clawhub
小巫debug日记2026/3/10

从零开发一个掘金自动发布 Skill,并上架 Clawhub 本文记录了一次完整的 Skill 开发旅程:从一句「帮我创建一个可以自动发布文章到掘金的 skill」开始,到最终成功上架 Clawhub,全程真实还原每一个关键决策和踩坑过程。 背景:为什么要做这个 Skill? 我日常运营一个 AI 资讯账号,每天需要将 Markdown 格式的文章发布到多个平台,包括微信公众号、小红书、掘金等。其中微信公众号和小红书已经有现成的 Skill 可以用,但掘金没有。 每次发布掘金都要: 打开


Word 中 MathType 启动慢、卡顿、卡死 | 由于某种原因,PowerPoint 无法加载MathType……
斐夷所非2026/3/2

注:本文为 “office 中 MathType 启动、加载异常” 相关合辑。 图片清晰度受引文原图所限。 略作重排,如有内容异常,请看原文。 Word 2013 中 MathType 窗口启动延迟问题分析与解决方案 香蕉君达 发布于 2026-02-19 12:12 1 现象描述 通过快捷键或功能区按钮在 Word 2013 中插入公式时,编辑窗口启动延迟时长约为 3~4 秒,对文档编辑流程造成干扰。 测试表明,若系统中已存在至少一个处于打开状态的 MathType 窗口,后续公式


SpringBoot多环境配置实战指南
北极的代码2026/2/22

前言:在之前的开发环境中要跟改配置,测试环境也要改,每次切换环境都要手动修改配置文件 常常发生"我们在本地能运行,怎么部署到服务器就报错"的情况,一不小心就把测试环境的配置提交到代码库。因此我们提出了多环境开发配置。 多环境开发配置: 在SpringBoot中,多环境配置的管理核心是利用Profile机制,它允许我们为不同的运行环境(开发,测试,生产)定义独立的配置,并在应用启动时动态的激活,从而实现配置等隔离与灵活切换。 核心实现方式:Profile 特定配置文件 总之就


聊一聊 CLI:为什么真正的工程能力,都藏在命令行里?
G探险者2026/2/14

大家好,我是G探险者! 今天我们来聊一聊CLI。 在很多人眼里,命令行(CLI,Command Line Interface)是“黑框 + 英文命令”的代名词。 对普通用户来说,它晦涩、难记、不友好。 但对工程师来说—— CLI 是系统可编排能力的起点,是自动化的基础设施,是 DevOps 的地基。 今天我们不从“怎么用命令”讲起,而是聊一聊: CLI 是怎么诞生的? 为什么它没有被 GUI 取代? 为什么所有现代基础设施几乎都优先设计 CLI? 为什么 CLI 是工程能力的分水岭?

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客