字节golang后端二面

前端接口使用restful格式,post与get的区别是什么?

HTTP网络返回的状态码有哪些?

go语言切片与数组的区别是什么?

MySQL实现并发安全避免两个事务同时对一个记录写操作的手段有哪些?

如何实现业务的幂等性(在golang代码中如何避免消息重复或处理已出现的消息重复)?

如何设置redis分布式锁?


SETNX 是 Redis 中的一个命令,全称为 "SET if Not eXists"。它的作用是在键不存在时设置键的值。具体来说,SETNX 的功能如下:

语法

plaintext 复制代码
SETNX key value

返回值

  • 1: 如果键成功设置(即键不存在)。
  • 0: 如果键已经存在,设置失败。

用法示例

1. 设置一个键
plaintext 复制代码
SETNX my_key "some_value"
  • 如果 my_key 不存在,则它的值被设置为 "some_value",返回 1。
  • 如果 my_key 已经存在,则不做任何操作,返回 0。
2. 用于分布式锁

SETNX 常用于实现分布式锁,因为它可以确保只有一个进程可以获得锁。例如:

python 复制代码
import redis

# 连接到 Redis
client = redis.StrictRedis(host='localhost', port=6379, db=0)

# 尝试获取锁
if client.setnx("my_lock", "some_unique_value"):
    print("Lock acquired")
    # 执行临界区代码
    
    # 释放锁
    client.delete("my_lock")
else:
    print("Lock already acquired")

注意事项

  • 不适合复杂操作 : SETNX 只能用于简单的键值设置,不能用于复杂的条件判断。
  • 锁的有效性 : 在实现分布式锁时,结合使用 SETNX 和设置过期时间可以防止死锁。

ThreadLocal

ThreadLocal 是 Java 中的一个类,用于提供线程局部变量。每个使用 ThreadLocal 的线程都可以独立地存储和访问自己的变量副本,而不会与其他线程共享。以下是关于 ThreadLocal 的详细解释:

1. 基本概念

  • 线程局部变量 : 每个线程在访问 ThreadLocal 变量时,会得到自己独立的副本。这意味着一个线程对 ThreadLocal 变量的修改不会影响其他线程。
  • 存储位置 : ThreadLocal 变量存储在每个线程的 ThreadLocalMap 中。

2. 使用方法

2.1 创建 ThreadLocal 变量
java 复制代码
ThreadLocal<String> threadLocalVar = new ThreadLocal<>();
2.2 设置值

使用 set() 方法将值存储到当前线程的 ThreadLocal 变量中。

java 复制代码
threadLocalVar.set("Hello, ThreadLocal!");
2.3 获取值

使用 get() 方法从当前线程的 ThreadLocal 变量中获取值。

java 复制代码
String value = threadLocalVar.get(); // 返回 "Hello, ThreadLocal!"
2.4 清除值

使用 remove() 方法可以清除当前线程的 ThreadLocal 变量值。

java 复制代码
threadLocalVar.remove();

3. 示例代码

以下是一个使用 ThreadLocal 的简单示例:

java 复制代码
public class ThreadLocalExample {
    private static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Runnable task = () -> {
            Integer value = threadLocalValue.get();
            value++;
            threadLocalValue.set(value);
            System.out.println(Thread.currentThread().getName() + ": " + threadLocalValue.get());
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();
    }
}

4. 适用场景

  • 用户会话信息 : 在 Web 应用中,可以使用 ThreadLocal 存储每个请求的用户信息。
  • 数据库连接: 每个线程可以持有自己的数据库连接,避免共享连接导致的线程安全问题。
  • 性能优化: 减少对象的创建和销毁,避免频繁的上下文传递。

5. 注意事项

  • 内存泄漏 : 如果不调用 remove() 方法,线程局部变量可能会导致内存泄漏,尤其在使用线程池时。
  • 不适合跨线程使用 : ThreadLocal 变量仅在创建它的线程中可见,其他线程无法访问。
  • 调试困难 : 使用 ThreadLocal 可能会让调试变得复杂,因为变量的状态依赖于线程的生命周期。

总结

ThreadLocal 是一个强大的工具,适用于需要线程隔离的数据存储场景。合理使用可以提高性能和简化代码,但也需要注意内存管理和线程安全问题。

超卖问题怎么解决?

  • redis使用lua脚本判断和扣减库存
  • 使用redis队列避免数据不一致的问题(先将数据存入redis队列中,从redis队列中取出记录添加到数据库)

后端如何对一个请求鉴权?为什么JWT要双签发?被截获了怎么办?如果人为地想要禁止某个用户的访问应该怎么做?

一、后端请求鉴权流程

后端通常通过以下步骤验证请求合法性:

  1. Token 提取
    从请求头(如 Authorization: Bearer <token>)或 Cookie 中获取 JWT。
  2. 签名验证
    用预设密钥验证 JWT 签名是否被篡改(例如使用 HMAC 或 RSA 算法)。
  3. 有效期检查
    校验 exp(过期时间)和 nbf(生效时间)字段。
  4. 业务逻辑鉴权
    • 从 JWT 解析用户角色/权限(如 role: admin)。
    • 对比请求资源所需的权限(如 RBAC 模型)。
  5. 敏感操作二次验证
    关键操作(如支付)要求重新输入密码或 2FA 验证。
python 复制代码
# Python 伪代码示例(使用 PyJWT)
def verify_request(request):
    token = request.headers.get("Authorization").split("Bearer ")[1]
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        if payload["role"] != "admin":
            raise PermissionError("无权访问")
        return True
    except jwt.ExpiredSignatureError:
        raise Unauthorized("Token过期")

二、JWT 双签发机制及作用

双签发(Dual-Issuer) 指同时使用两个密钥签发 Token:

  1. 主密钥(Primary Key)
    • 用于签发长期有效的 访问令牌(Access Token)(如有效期 1 小时)。
  2. 从密钥(Secondary Key)
    • 用于签发短期有效的 刷新令牌(Refresh Token)(如有效期 7 天)。

✔ 核心目的:安全性隔离

  • 访问令牌泄露风险高:频繁在网络传输,暴露概率大。
  • 刷新令牌严格保护:仅用于获取新访问令牌,存储于安全环境(如 HttpOnly Cookie)。
  • 攻击窗口最小化:即使 Access Token 被截获,有效期短且无法直接获取新 Token。

三、JWT 被截获的应对措施

攻击类型 防御方案 示例
中间人窃听 强制 HTTPS + HSTS 头 服务器配置 TLS 1.3
客户端脚本窃取 HttpOnly Cookie 存储 + XSS 防护 设置 Set-Cookie: HttpOnly; Secure
Token 泄露 短期有效期 + 刷新令牌轮转 Access Token 有效期 ≤15 分钟
重放攻击 JTI(JWT ID)唯一标识 + 服务端黑名单 使用 Redis 记录已使用的 JTI

刷新令牌轮转示例

每次用 Refresh Token 获取新 Access Token 时,同步生成新 Refresh Token 并作废旧令牌。这样即使旧刷新令牌被截获,攻击者只能使用一次。


四、禁止特定用户访问的解决方案

1. 短期封禁:令牌黑名单(Token Blacklist)
  • 适用场景:立即踢出已登录用户。
  • 实现方式
    • 用户注销或封禁时,将 JWT 的 jti(唯一ID)加入 Redis 黑名单。
    • 鉴权时校验 jti 是否在黑名单中。
  • 优点:实时生效。
  • 缺点:增加数据库查询开销。
python 复制代码
# 封禁用户时
redis.set(f"blacklist:{jti}", "1", ex=ACCESS_TOKEN_EXPIRE)

# 鉴权时检查
if redis.exists(f"blacklist:{jti}"):
    raise Forbidden("用户已被封禁")
2. 长期封禁:用户状态标记
  • 适用场景:永久禁止访问。
  • 实现方式
    • 在用户数据库添加 is_active 字段。
    • 鉴权时查询用户状态(注意缓存用户信息避免频繁查库)。
  • 优点:一劳永逸。
  • 缺点:状态变更后需等待 Token 自然过期。
3. 强制令牌失效:刷新令牌回收
  • 封禁用户时,删除该用户的刷新令牌
  • 用户 Access Token 过期后无法续签,自动退出。

五、最佳实践总结

  1. 双签发必要性
    ✅ 隔离高风险令牌(Access Token)与高价值令牌(Refresh Token)。
  2. 防截获组合拳
    ✅ HTTPS + 短有效期 + HttpOnly Cookie + 刷新令牌轮转。
  3. 封禁用户策略
    • 紧急场景:令牌黑名单(实时生效)。
    • 永久封禁:标记用户状态 + 回收刷新令牌
  4. 性能优化
    • 黑名单用 Redis 存储并设置自动过期(与 Token 有效期对齐)。
    • 用户状态变化时清理缓存(如 Redis 中的用户信息)。

关键原则 :JWT 本身无状态,需通过黑名单/用户状态引入必要状态控制,在安全性和性能间取得平衡。




相关推荐
志辉AI编程7 分钟前
别人还在入门,你已经精通!Claude Code进阶必备14招
后端·ai编程
阿猿收手吧!8 分钟前
【计算机网络】HTTP1.0 HTTP1.1 HTTP2.0 QUIC HTTP3 究极总结
开发语言·计算机网络
JAVA学习通9 分钟前
图书管理系统(完结版)
java·开发语言
代码老y14 分钟前
Spring Boot项目中大文件上传的高级实践与性能优化
spring boot·后端·性能优化
paishishaba16 分钟前
处理Web请求路径参数
java·开发语言·后端
七七七七0717 分钟前
C++类对象多态底层原理及扩展问题
开发语言·c++
程序无bug20 分钟前
Java中的8中基本数据类型转换
java·后端
雨落倾城夏未凉24 分钟前
8.Qt文件操作
c++·后端·qt
51727 分钟前
Django中序列化与反序列化
后端·python·django