统一身份认证
认证协议对比
| 协议 | 传输方式 | 适用场景 | 特点 |
|---|---|---|---|
| CAS | 重定向 + Ticket | 企业 SSO | 单点登录经典方案 |
| LDAP | TCP/IP | 组织架构管理 | 存储用户 / 部门树 |
| SSO | 多种方式 | 多系统免登录 | 核心是共享会话 |
| SAML | XML + 断言 | 企业级联合认证 | 跨组织信任 |
| OIDC | JWT + OAuth2 | 现代互联网 | 轻量、OAuth2 扩展 |
CAS(Central Authentication Service)
协议流程
1. 用户访问 App A(无 Ticket)
→ 302 重定向到 CAS Server?service=https://appA/callback
2. CAS Server 展示登录页
→ 用户提交用户名密码
3. CAS Server 生成 TGT(全局 Ticket)
→ 生成 ST(Service Ticket)
→ 302 回 AppA?ticket=ST-xxx
4. App A 用 ST 向 CAS Server 验证
→ CAS Server 返回用户名 + 属性
5. 用户访问 App B
→ App B 发现无 Ticket
→ 302 到 CAS Server?service=https://appB/callback
→ CAS Server 检查 TGT 存在
→ 生成新 ST → 跳转 App B
→ 用户免登录部署架构
┌─────────────┐
│ CAS Server │ ← 认证中心(单点)
└──────┬──────┘
┌─────────┼─────────┐
│ │ │
┌───▼───┐ ┌──▼───┐ ┌──▼───┐
│ App A │ │App B │ │App C │
└───────┘ └──────┘ └──────┘
(ST) (ST) (ST)LDAP 统一认证
目录结构
dc=example,dc=com
├─ ou=people
│ ├─ uid=zhangsan
│ │ ├─ cn: 张三
│ │ ├─ mail: zhangsan@example.com
│ │ └─ memberOf: cn=admin,ou=groups
│ └─ uid=lisi
└─ ou=groups
├─ cn=admin
└─ cn=userSpring Boot + LDAP 集成
yaml
spring:
ldap:
urls: ldap://localhost:389
base: dc=example,dc=com
username: cn=admin,dc=example,dc=com
password: adminjava
@Configuration
public class LdapSecurityConfig {
@Bean
public AuthenticationProvider ldapProvider(LdapContextSource source) {
LdapAuthenticationProvider provider =
new LdapAuthenticationProvider(
new BindAuthenticator(source),
new DefaultLdapAuthoritiesPopulator(source, "ou=groups"));
provider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
return provider;
}
}多因素认证(MFA)
认证因素分类
| 类型 | 示例 |
|---|---|
| 知识因素 | 密码、PIN 码 |
| 持有因素 | 手机(短信/验证器)、硬件 Key |
| 固有因素 | 指纹、人脸、声纹 |
| 位置因素 | IP 地理围栏 |
| 行为因素 | 击键节奏、鼠标轨迹 |
TOTP 实现
java
public class TotpUtil {
private static final long TIME_STEP = 30; // 30秒
public static String generateCode(String secret) {
long counter = System.currentTimeMillis() / 1000 / TIME_STEP;
byte[] data = ByteBuffer.allocate(8).putLong(counter).array();
byte[] hash = new HmacUtils("HmacSHA1", secret).hmac(data);
int offset = hash[hash.length - 1] & 0xF;
int code = (hash[offset] & 0x7F) << 24 |
(hash[offset + 1] & 0xFF) << 16 |
(hash[offset + 2] & 0xFF) << 8 |
(hash[offset + 3] & 0xFF);
return String.format("%06d", code % 1_000_000);
}
}