2、身份认证机制:基于密码的认证、Kerberos协议、OAuth 2.0与OpenID Connect
身份认证,说白了就是回答一个问题:「你凭什么说你是你?」
在分布式系统里,这个问题比单机环境复杂得多。用户可能从手机、浏览器、IoT设备同时发起请求,服务之间也要互相证明身份。我做了十几年安全架构,见过太多因为认证没做好而翻车的案例。今天咱们就把四种主流认证机制掰开揉碎了讲清楚。
2.1 基于密码的认证:最古老也最危险
密码认证是最基础的方式。用户输入用户名和密码,服务端验证。听起来简单,但坑特别多。
我个人习惯把密码存储分为三个等级:
- 明文存储 —— 绝对禁止。我在项目中遇到过某初创公司直接把密码记在数据库里,结果被拖库后用户数据全裸奔。
- 哈希存储 —— 常用做法。但别用MD5、SHA-1,它们已经被破解了。建议用bcrypt、scrypt或Argon2。
- 加盐哈希 —— 这才是正确姿势。每个用户随机生成一个盐值,和密码拼接后再哈希。这样就算两个用户密码相同,存储的哈希值也不一样。
// 伪代码示例:加盐哈希
String salt = generateRandomSalt(); // 每个用户独立
String hash = bcrypt(salt + password);
store(salt, hash);
传输过程也要加密。千万别用HTTP明文传密码。我建议至少用HTTPS,如果条件允许,前端先做一次哈希再传输,服务端再哈希一次。这叫「双重哈希」,能防止传输过程中被中间人截获原始密码。
2.2 Kerberos协议:企业级认证的经典方案
Kerberos是MIT开发的认证协议,在Windows域控和很多企业内部系统里广泛使用。它的核心思想是:不直接传输密码,而是用票据(Ticket)来证明身份。
你想想看,如果每次登录都要把密码发出去,风险太大了。Kerberos引入了一个可信第三方——KDC(密钥分发中心)。
流程大致是这样的:
- 用户向KDC发送认证请求,包含自己的身份信息。
- KDC验证用户身份(通常通过密码哈希),返回一个TGT(票据授权票据)和会话密钥。
- 用户拿着TGT去申请访问某个服务的票据。
- KDC验证TGT有效后,发放服务票据。
- 用户用服务票据访问目标服务,服务验证票据后放行。
我记得有一次帮一家银行做架构评审,他们内部系统用了Kerberos,但票据有效期设成了24小时。结果有员工离职后,他的票据还能用大半天。嗯,这里要注意:票据有效期一定要短,一般建议8小时以内,敏感系统可以设到1小时。
Kerberos的缺点也很明显:
- 所有节点必须时间同步(误差通常不超过5分钟)。
- KDC是单点故障,挂了整个系统都认证不了。
- 不适合互联网场景,因为客户端和服务端需要预先共享密钥。
2.3 OAuth 2.0:授权协议的工业标准
OAuth 2.0其实不是认证协议,是授权协议。它解决的是「用户允许第三方应用访问自己在某服务上的资源」这个问题。比如你用微信登录某个网站,网站请求获取你的微信昵称和头像,这就是OAuth 2.0的典型场景。
OAuth 2.0定义了四种授权模式:
| 模式 | 适用场景 | 安全等级 |
|---|---|---|
| 授权码模式 | 有后端的Web应用 | 高 |
| 隐式模式 | 纯前端应用(已不推荐) | 低 |
| 密码模式 | 高度信任的客户端 | 中 |
| 客户端凭证模式 | 服务间通信 | 高 |
我个人最常用的是授权码模式。流程是这样的:
- 用户点击「微信登录」,浏览器跳转到微信授权页面。
- 用户确认授权后,微信返回一个授权码(code)到你的后端。
- 后端用授权码向微信服务器换取access_token。
- 后端用access_token调用微信API获取用户信息。
// 后端交换授权码的示例
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=授权码
&redirect_uri=回调地址
&client_id=应用ID
&client_secret=应用密钥
2.4 OpenID Connect:在OAuth 2.0之上做认证
OAuth 2.0只管授权,不管认证。它不知道「用户是谁」,只知道「用户授权了哪些权限」。OpenID Connect(OIDC)就是在OAuth 2.0之上加了一层身份认证层。
OIDC引入了一个新的token类型——ID Token。这是一个JWT(JSON Web Token),里面包含了用户的身份信息,比如用户ID、姓名、邮箱等。
ID Token的结构是这样的:
{
"iss": "https://accounts.google.com", // 签发者
"sub": "1234567890", // 用户唯一标识
"aud": "your-app-id", // 接收方
"exp": 1516239022, // 过期时间
"iat": 1516239022, // 签发时间
"name": "张三",
"email": "zhangsan@example.com"
}
你想想看,有了ID Token,应用不需要再额外调用API去查用户信息了。token本身就已经告诉了你「用户是谁」。而且JWT是经过签名的,应用可以验证它是否被篡改过。
我建议在微服务架构中优先使用OIDC。每个服务只需要验证JWT的签名,不需要每次都去认证中心查询。这样能大幅降低认证中心的压力。
- 使用RS256(RSA签名)而不是HS256(HMAC签名)。RS256用公钥验证,私钥只有认证中心持有,更安全。
- JWT的payload不要放敏感信息,因为payload只是base64编码,不是加密的。
- 一定要验证JWT的签名、过期时间、签发者和接收方。
嗯,最后总结一下我的个人看法:
- 内部系统用Kerberos,稳定可靠。
- 互联网应用用OAuth 2.0 + OIDC,灵活且标准。
- 密码认证作为兜底方案,但一定要做好加盐哈希和传输加密。
- 不管用哪种方案,永远不要信任客户端。所有认证逻辑必须在服务端完成。
我曾经见过一个团队,为了省事把JWT的密钥写在前端代码里。结果被逆向工程后,攻击者可以伪造任意用户的token。这种低级错误,真的不应该犯。
下一章我们会聊授权机制,讲清楚「你是谁之后,你能做什么」。到时候见。