鉴权是微型博客的「大门」——它回答「你是谁」并给你一把进门的钥匙。从最朴素的「用户名 + 密码」,到现代平台的「OAuth2 第三方登录 + 刷新令牌」,鉴权方案的每一次演进都在平衡安全、体验与可扩展性。
本文系统讲解:密码哈希、Session vs JWT、OAuth2 登录、访问/刷新令牌、设备管理。
一、注册与登录流程
1.1 标准流程
注册:用户名/邮箱 + 密码 → 校验唯一性 → 哈希存储 → 建用户
登录:输入凭据 → 校验密码哈希 → 发令牌 → 建立会话
会话:每次请求带令牌 → 校验 → 放行
退出:吊销令牌 / 清除会话
1.2 用户表
CREATE TABLE users (
id BIGINT PRIMARY KEY,
username VARCHAR(30) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE,
password_hash VARCHAR(255), -- 可为空(第三方登录用户)
avatar_url TEXT,
created_at TIMESTAMPTZ DEFAULT now(),
last_login_at TIMESTAMPTZ,
status SMALLINT DEFAULT 1
);
1.3 密码校验
// 注册时哈希
hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
user.PasswordHash = string(hash)
// 登录时比对
err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password))
if err != nil { /* 密码错误 */ }
一句话总结:鉴权的第一步是把「明文密码」永远拒之门外——用带随机盐的慢哈希(bcrypt/argon2)存储,比对时同样走哈希函数。
二、密码哈希与安全存储
2.1 为什么不能存明文
数据库泄露 → 明文密码全部暴露 → 撞库(同一密码多平台复用)→ 连锁损失
慢哈希的目的:即使拿到哈希,也难以暴力破解(每次尝试都昂贵)
2.2 bcrypt vs argon2
| 算法 | 特点 | 适用 |
|---|---|---|
| bcrypt | 内置盐、可调 cost | 最常用,库支持广 |
| argon2 | 内存硬(抗 GPU/ASIC) | 最高安全要求 |
| scrypt | 内存硬 | 次优选择 |
| MD5/SHA1 | 快(不适合密码) | ❌ 绝不用于密码 |
// argon2 示例(更安全)
salt := make([]byte, 16)
rand.Read(salt)
hash := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
// 存储格式:$argon2id$v=19$m=65536,t=1,p=4$<salt>$<hash>
2.3 密码策略
- 最小长度(≥8),不强求复杂字符(长度优先)
- 尝试次数限制(登录限流,防爆破)
- 泄露检测:检查常见弱密码/撞库名单
- 强制重置:可疑登录后
一句话总结:密码哈希的准则是「慢且带盐」——bcrypt/argon2 让每次猜测都昂贵,随机盐防彩虹表,长度与限流补足策略。
三、Session 与 JWT
3.1 Session(服务端会话)
登录 → 服务端生成 session_id → 存 Redis/内存 → 下发 cookie
请求 → cookie 携带 session_id → 服务端查 session → 校验 → 放行
| 优点 | 缺点 |
|---|---|
| 可主动吊销 | 服务端要存状态 |
| 可存任意数据 | 分布式需共享 session 存储 |
3.2 JWT(无状态令牌)
登录 → 服务端签发 JWT(header.payload.signature)→ 下发
请求 → Authorization: Bearer <jwt> → 验签 → 放行(无需查库)
| 优点 | 缺点 |
|---|---|
| 无状态,易水平扩展 | 默认难主动吊销 |
| 可携带用户信息 | payload 过大膨胀 |
3.3 对比与选型
| 维度 | Session | JWT |
|---|---|---|
| 状态存储 | 服务端(Redis) | 无状态 |
| 吊销 | 即时 | 需黑名单/短过期 |
| 扩展性 | 需共享存储 | 天然水平扩展 |
| 安全 | cookie 需防 CSRF | token 需防 XSS 窃取 |
| 微型博客选型 | 单机/中小 | 多实例/开放 API |
实践建议:
面向 API/第三方 → JWT
面向 Web 页面 → Session(HttpOnly Cookie)
混合:JWT + Redis 黑名单(吊销兜底)
一句话总结:Session 用服务端状态换「可吊销」,JWT 用无状态换「易扩展」——微型博客常见做法是 Session 管 Web、JWT 管 API。
四、OAuth2 与第三方登录
4.1 为什么需要
用户不想注册第三个账号 → 「用 Google 登录」「用 GitHub 登录」
OAuth2 授权码流程让平台把「身份验证」委托给第三方,同时获取受控权限
4.2 授权码流程
1. 用户点「用 Google 登录」→ 跳转 Google
2. Google 登录并同意 → 回调 code
3. 后端用 code + client_secret 换 access_token
4. 用 access_token 调 Google 用户信息接口
5. 匹配/创建本地用户 → 建立本地会话
4.3 第三方账号绑定
CREATE TABLE oauth_accounts (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
provider VARCHAR(20) NOT NULL, -- google/github
provider_uid VARCHAR(100) NOT NULL, -- 第三方唯一 id
access_token VARCHAR(255),
refresh_token VARCHAR(255),
expires_at TIMESTAMPTZ,
UNIQUE (provider, provider_uid)
);
4.4 安全要点
- state 参数防 CSRF(回调时校验)
- 只申请最小 scope(profile/email)
- provider_uid 是唯一键,防重复绑定
- 第三方 token 加密存储或仅内存使用
- 邮箱已验证才作为登录标识
一句话总结:OAuth2 第三方登录把「验证」外包给平台,但本地必须用 provider_uid 做唯一关联、用 state 防 CSRF、用最小 scope 控权限。
五、访问令牌与刷新令牌
5.1 为什么分两种
- 访问令牌(Access Token):短期(15min~2h),每次请求携带
- 刷新令牌(Refresh Token):长期(7~30天),仅用于换新访问令牌
分离的好处:
- 访问令牌短命 → 泄露影响小
- 刷新令牌长命 → 用户体验好(少登录)
- 刷新令牌可吊销 → 找回被盗会话
5.2 刷新流程
访问令牌过期
→ 客户端用 refresh_token 调 /oauth/token
→ 服务端校验 refresh_token + 轮换(旧作废,发新的)
→ 返回新 access_token + 新 refresh_token
5.3 刷新令牌轮换
- 每次刷新都发新 refresh_token,旧的立即失效(防重放)
- 检测到旧 token 重复使用 → 视为被盗 → 吊销整条会话链
- refresh_token 存储:HttpOnly Cookie 或服务端
5.4 实现要点
type TokenPair struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int64 `json:"expires_in"`
}
// access: 15min JWT
// refresh: 14天 随机字符串存 Redis(可吊销)
一句话总结:双令牌把「频繁验证」与「长期登录」解耦——短命 access 降低泄露面,轮换的 refresh 让长期会话既方便又可吊销。
六、设备管理与风控
6.1 设备会话
CREATE TABLE sessions (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
device_id VARCHAR(64), -- 设备指纹
platform VARCHAR(20), -- ios/android/web
refresh_token_hash VARCHAR(128),
ip VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMPTZ,
last_seen_at TIMESTAMPTZ,
revoked BOOLEAN DEFAULT FALSE
);
6.2 登录风控
- 异常 IP / 异地登录 → 二次验证或告警
- 尝试次数限制 + 锁定(短时连续失败)
- 新设备登录 → 通知用户
- 高风险操作(改邮箱、改密码)→ 重新鉴权
6.3 会话管理
- 「退出所有设备」→ 批量吊销 sessions
- 「查看活跃会话」→ 按设备列表
- 改密码 → 吊销全部会话(安全实践)
一句话总结:会话不止「发出去」,还要「可管理」——设备维度的会话表支撑查看、吊销、风控,是鉴权的运营面。
七、安全加固清单
| 主题 | 做法 |
|---|---|
| 密码 | bcrypt/argon2 + 随机盐,永不存明文 |
| 传输 | 全站 HTTPS,禁止明文登录 |
| Web 会话 | HttpOnly + SameSite Cookie 防 XSS/CSRF |
| API | Bearer Token 放 Authorization 头 |
| 刷新令牌 | 轮换 + 存储哈希 + 检测重放即吊销 |
| 风控 | 限流、异地告警、新设备确认 |
| 日志 | 登录/登出/失败审计日志 |
鉴权是微型博客的第一道防线:密码哈希守住账号,双令牌平衡安全与体验,OAuth2 开放第三方入口,会话管理提供运营面。把这几层做扎实,用户的账号就守住了。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。