字节golang后端二面
前端接口使用restful格式,post与get的区别是什么?
HTTP网络返回的状态码有哪些?
go语言切片与数组的区别是什么?
MySQL实现并发安全避免两个事务同时对一个记录写操作的手段有哪些?
如何实现业务的幂等性(在golang代码中如何避免消息重复或处理已出现的消息重复)?
如何设置redis分布式锁?
SETNX
是 Redis 中的一个命令,全称为 “SET if Not eXists”。它的作用是在键不存在时设置键的值。具体来说,SETNX
的功能如下:
语法
SETNX key value
返回值
- 1: 如果键成功设置(即键不存在)。
- 0: 如果键已经存在,设置失败。
用法示例
1. 设置一个键
SETNX my_key "some_value"
- 如果
my_key
不存在,则它的值被设置为"some_value"
,返回 1。 - 如果
my_key
已经存在,则不做任何操作,返回 0。
2. 用于分布式锁
SETNX
常用于实现分布式锁,因为它可以确保只有一个进程可以获得锁。例如:
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
变量
ThreadLocal<String> threadLocalVar = new ThreadLocal<>();
2.2 设置值
使用 set()
方法将值存储到当前线程的 ThreadLocal
变量中。
threadLocalVar.set("Hello, ThreadLocal!");
2.3 获取值
使用 get()
方法从当前线程的 ThreadLocal
变量中获取值。
String value = threadLocalVar.get(); // 返回 "Hello, ThreadLocal!"
2.4 清除值
使用 remove()
方法可以清除当前线程的 ThreadLocal
变量值。
threadLocalVar.remove();
3. 示例代码
以下是一个使用 ThreadLocal
的简单示例:
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要双签发?被截获了怎么办?如果人为地想要禁止某个用户的访问应该怎么做?
一、后端请求鉴权流程
后端通常通过以下步骤验证请求合法性:
- Token 提取
从请求头(如Authorization: Bearer <token>
)或 Cookie 中获取 JWT。 - 签名验证
用预设密钥验证 JWT 签名是否被篡改(例如使用 HMAC 或 RSA 算法)。 - 有效期检查
校验exp
(过期时间)和nbf
(生效时间)字段。 - 业务逻辑鉴权
- 从 JWT 解析用户角色/权限(如
role: admin
)。 - 对比请求资源所需的权限(如 RBAC 模型)。
- 从 JWT 解析用户角色/权限(如
- 敏感操作二次验证
关键操作(如支付)要求重新输入密码或 2FA 验证。
# 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 Trueexcept jwt.ExpiredSignatureError:raise Unauthorized("Token过期")
二、JWT 双签发机制及作用
双签发(Dual-Issuer) 指同时使用两个密钥签发 Token:
- 主密钥(Primary Key)
- 用于签发长期有效的 访问令牌(Access Token)(如有效期 1 小时)。
- 从密钥(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
是否在黑名单中。
- 用户注销或封禁时,将 JWT 的
- 优点:实时生效。
- 缺点:增加数据库查询开销。
# 封禁用户时
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 过期后无法续签,自动退出。
五、最佳实践总结
- 双签发必要性
✅ 隔离高风险令牌(Access Token)与高价值令牌(Refresh Token)。 - 防截获组合拳
✅ HTTPS + 短有效期 + HttpOnly Cookie + 刷新令牌轮转。 - 封禁用户策略
- 紧急场景:令牌黑名单(实时生效)。
- 永久封禁:标记用户状态 + 回收刷新令牌。
- 性能优化
- 黑名单用 Redis 存储并设置自动过期(与 Token 有效期对齐)。
- 用户状态变化时清理缓存(如 Redis 中的用户信息)。
关键原则:JWT 本身无状态,需通过黑名单/用户状态引入必要状态控制,在安全性和性能间取得平衡。