二、认证机制:密码策略设计、多因素认证(MFA)实现、单点登录(SSO)集成方案

好,咱们进入第二个核心话题——认证机制。

说实话,OA系统里80%的安全事件,都出在认证这个环节。我见过太多公司,花大价钱买防火墙、上IDS,结果员工密码还是「123456」。你说这防火墙防得住吗?防不住啊。

这一章,咱们就聊聊怎么把认证这件事做扎实。我把它拆成三块:密码策略、多因素认证、单点登录。这三块是递进关系,缺一不可。

2.1 密码策略设计:别让员工用「password」

密码策略,听起来简单,做起来全是坑。我早期做的一个项目,客户要求密码必须包含大小写、数字、特殊字符,长度至少12位。结果呢?员工全把密码贴在显示器上。你想想看,这反而更不安全了。

所以,好的密码策略要平衡安全性和可用性。我个人习惯用这么几招:

  • 长度优先,复杂度为辅:我建议密码最小长度设为8位,推荐12位。长度比复杂度更重要。一个12位的纯数字密码,比一个6位的「P@ss1」难破解得多。
  • 禁止常见弱密码:维护一个黑名单,把「123456」「password」「admin123」这些统统拉黑。我在项目中遇到过,有员工用「company2023」这种,其实也很危险。
  • 密码过期策略要灵活:别搞90天强制过期。NIST最新的指南都说了,频繁改密码反而让用户用更弱的密码。我建议:除非有泄露风险,否则不强制过期。或者设置一个「密码年龄」提醒,但不强制改。
  • 历史密码去重:至少记住最近5个密码,防止用户来回换。

核心原则:密码策略的目标不是让用户记住复杂密码,而是让攻击者猜不到。

这里给个参考配置:

// 密码策略配置示例(伪代码)
passwordPolicy: {
  minLength: 8,
  maxLength: 128,
  requireUppercase: false,  // 不强制,但鼓励
  requireLowercase: false,
  requireDigit: true,
  requireSpecialChar: false,
  blacklist: ['123456', 'password', 'admin', 'company2023'],
  historyCount: 5,
  expiryDays: 0,  // 0表示不过期
  maxLoginAttempts: 5,
  lockoutDuration: 15  // 分钟
}

嗯,这里要注意一点:登录失败锁定策略。我见过一个OA系统,连续输错3次就锁账号,结果员工天天找IT解锁。我建议设5次,锁定15分钟。既防暴力破解,又不影响正常使用。

2.2 多因素认证(MFA)实现:加一把锁

密码再强,也怕泄露。钓鱼、撞库、键盘记录……手段太多了。所以,MFA是必须的。

MFA说白了就是「你知道的 + 你拥有的 + 你本身的」。密码是「你知道的」,那第二个因素选什么呢?

我个人最推荐的是TOTP(基于时间的一次性密码)。为什么?因为它不依赖短信网关,没有运营商延迟,而且可以离线生成。Google Authenticator、Microsoft Authenticator都支持。

实现起来也不复杂:

// TOTP 生成与验证(Python示例)
import pyotp
import qrcode

# 1. 生成密钥(每个用户唯一)
secret = pyotp.random_base32()  # 例如:'JBSWY3DPEHPK3PXP'

# 2. 生成二维码(供用户扫描)
totp = pyotp.TOTP(secret)
uri = totp.provisioning_uri(name='user@company.com', issuer_name='OA系统')
qrcode.make(uri).save('qrcode.png')

# 3. 验证用户输入的6位码
def verify_totp(user_input, secret):
    totp = pyotp.TOTP(secret)
    return totp.verify(user_input, valid_window=1)  // 允许前后1个时间窗口

避坑指南:我曾经在项目中直接用了短信验证码作为第二因素,结果发现短信有延迟,用户经常输错。后来换成TOTP,体验好多了。而且TOTP没有额外成本,短信每条都要钱。

那什么时候强制MFA?我建议:

  • 所有管理员账号:必须开启MFA,没得商量。
  • 普通用户:敏感操作时触发,比如修改密码、查看薪资、审批大额报销。
  • 异地登录:检测到IP地址变化或新设备时,要求MFA验证。

还有一个细节:记住设备。用户第一次MFA验证后,可以生成一个设备Token,有效期30天。这样用户不用每次登录都输验证码,体验好很多。

2.3 单点登录(SSO)集成方案:一次登录,到处跑

公司里系统多了,每个都让用户记密码,不现实。SSO就是解决这个问题的。

OA系统做SSO,主流方案有两个:OAuth 2.0 + OIDCSAML 2.0。我个人更推荐OIDC,因为它是基于OAuth 2.0的,更轻量,适合现代应用。

SSO的核心流程是这样的:

  1. 用户访问OA系统,未登录。
  2. OA系统把用户重定向到身份提供商(IdP),比如Azure AD、Okta、Keycloak。
  3. 用户在IdP登录(如果已登录则跳过)。
  4. IdP生成一个ID Token(JWT格式),返回给OA系统。
  5. OA系统验证Token,提取用户信息,创建本地会话。

这里有个关键点:Token验证。我见过有人直接信任前端传来的Token,这是大忌。后端必须验证Token的签名、过期时间、发行者、受众。

// JWT Token 验证(Node.js示例)
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

const client = jwksClient({
  jwksUri: 'https://your-idp.com/.well-known/jwks.json'
});

function verifyToken(token) {
  return new Promise((resolve, reject) => {
    // 1. 获取公钥
    client.getSigningKey((err, key) => {
      if (err) return reject(err);
      const publicKey = key.getPublicKey();
      
      // 2. 验证Token
      jwt.verify(token, publicKey, {
        algorithms: ['RS256'],
        issuer: 'https://your-idp.com/',
        audience: 'your-oa-client-id'
      }, (err, decoded) => {
        if (err) return reject(err);
        resolve(decoded);
      });
    });
  });
}

警告:千万不要自己实现OAuth/OIDC协议。直接用成熟的库,比如Spring Security、Passport.js、python-social-auth。协议细节太多,自己写容易出安全漏洞。

集成SSO时,有几个坑要注意:

  • 会话管理:OA系统自己的会话和IdP的会话要同步。用户从IdP登出时,OA系统也要登出。这需要实现「单点登出(SLO)」。
  • 用户映射:IdP返回的用户标识(比如邮箱)要和OA系统的用户表对应上。我建议用邮箱作为唯一标识,不要用用户名。
  • 降级方案:IdP挂了怎么办?OA系统要保留本地登录能力,作为降级方案。但注意,本地登录的密码强度要更高,且建议开启MFA。

最后,说一个我踩过的坑:Token过期时间。OIDC的ID Token有效期通常很短(比如1小时),但OA系统的会话可能持续8小时。所以,OA系统要用自己的会话管理,不要依赖IdP的Token有效期。IdP的Token只用来做初始认证,后续用OA自己的Session。

小结

这一章咱们聊了认证机制的三个层次:

  • 密码策略:长度优先,黑名单辅助,别搞强制过期。
  • 多因素认证:TOTP是首选,管理员强制,普通用户敏感操作触发。
  • 单点登录:OIDC是主流,Token验证必须做,别忘了降级方案。

下一章,咱们聊聊授权模型。认证只是第一步,授权才是真正决定「谁能干什么」的关键。到时候见。