1. 单点登录SSO概述

单点登录(Single Sign-On,SSO)是一种身份认证和授权管理机制,允许用户使用一组凭据(用户名和密码)访问多个相关但独立的软件系统。SSO的核心价值在于提升用户体验、简化身份管理、增强安全性、降低管理成本。本文将详细介绍企业级单点登录解决方案的原理、实现细节以及在Java企业级应用中的最佳实践。

1.1 单点登录SSO核心价值

  1. 用户体验提升: 用户只需登录一次即可访问所有授权系统
  2. 安全性增强: 集中化的身份认证和授权管理
  3. 管理成本降低: 统一的用户管理和权限控制
  4. 系统集成简化: 标准化的认证和授权流程
  5. 合规性支持: 满足企业安全合规要求
  6. 审计追踪: 完整的用户行为审计日志

1.2 单点登录SSO应用场景

  • 企业内部系统: 统一门户、OA系统、ERP系统集成
  • SaaS应用: 多租户应用的身份认证
  • 移动应用: 移动端统一登录
  • 第三方集成: 与外部系统的身份认证集成
  • 微服务架构: 微服务间的身份认证和授权

1.3 单点登录SSO实现方案

  • 基于Session的SSO: 传统Session共享方案
  • 基于Token的SSO: JWT Token认证方案
  • 基于CAS的SSO: 中央认证服务方案
  • 基于OAuth2的SSO: OAuth2授权框架
  • 基于SAML的SSO: SAML协议方案

2. 单点登录SSO基础实现

2.1 SSO配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* SSO配置类
* @author Java实战
*/
@Configuration
@EnableConfigurationProperties(SsoProperties.class)
public class SsoConfig {

@Autowired
private SsoProperties properties;

/**
* JWT工具类
* @return JWT工具类
*/
@Bean
public JwtUtil jwtUtil() {
return new JwtUtil(properties.getJwt());
}

/**
* Redis工具类
* @return Redis工具类
*/
@Bean
public RedisUtil redisUtil() {
return new RedisUtil(properties.getRedis());
}

/**
* SSO服务
* @return SSO服务
*/
@Bean
public SsoService ssoService() {
return new SsoService();
}

/**
* SSO认证服务
* @return SSO认证服务
*/
@Bean
public SsoAuthService ssoAuthService() {
return new SsoAuthService();
}

/**
* SSO授权服务
* @return SSO授权服务
*/
@Bean
public SsoAuthorizationService ssoAuthorizationService() {
return new SsoAuthorizationService();
}

/**
* SSO监控服务
* @return SSO监控服务
*/
@Bean
public SsoMonitorService ssoMonitorService() {
return new SsoMonitorService();
}

private static final Logger logger = LoggerFactory.getLogger(SsoConfig.class);
}

2.2 SSO属性配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/**
* SSO属性配置
* @author Java实战
*/
@Data
@ConfigurationProperties(prefix = "sso")
public class SsoProperties {

/**
* 是否启用SSO
*/
private boolean enableSso = true;

/**
* SSO服务名称
*/
private String serviceName = "sso-service";

/**
* SSO服务版本
*/
private String serviceVersion = "1.0.0";

/**
* JWT配置
*/
private JwtConfig jwt = new JwtConfig();

/**
* Redis配置
*/
private RedisConfig redis = new RedisConfig();

/**
* Session配置
*/
private SessionConfig session = new SessionConfig();

/**
* 安全配置
*/
private SecurityConfig security = new SecurityConfig();

/**
* 监控配置
*/
private MonitorConfig monitor = new MonitorConfig();

/**
* JWT配置类
*/
@Data
public static class JwtConfig {
/**
* JWT密钥
*/
private String secretKey = "sso-jwt-secret-key";

/**
* JWT过期时间(秒)
*/
private long expirationTime = 3600;

/**
* JWT刷新时间(秒)
*/
private long refreshTime = 7200;

/**
* JWT签发者
*/
private String issuer = "sso-service";

/**
* JWT受众
*/
private String audience = "sso-clients";

/**
* JWT算法
*/
private String algorithm = "HS256";
}

/**
* Redis配置类
*/
@Data
public static class RedisConfig {
/**
* Redis主机
*/
private String host = "localhost";

/**
* Redis端口
*/
private int port = 6379;

/**
* Redis密码
*/
private String password = "";

/**
* Redis数据库
*/
private int database = 0;

/**
* Redis连接超时时间(毫秒)
*/
private int connectionTimeout = 2000;

/**
* Redis读取超时时间(毫秒)
*/
private int readTimeout = 2000;

/**
* Redis最大连接数
*/
private int maxConnections = 100;

/**
* Redis最小空闲连接数
*/
private int minIdleConnections = 10;
}

/**
* Session配置类
*/
@Data
public static class SessionConfig {
/**
* Session过期时间(秒)
*/
private int sessionTimeout = 1800;

/**
* Session刷新时间(秒)
*/
private int refreshTimeout = 300;

/**
* Session存储类型
*/
private String storageType = "REDIS";

/**
* Session键前缀
*/
private String keyPrefix = "sso:session:";

/**
* Session清理间隔(秒)
*/
private int cleanupInterval = 600;
}

/**
* 安全配置类
*/
@Data
public static class SecurityConfig {
/**
* 是否启用安全
*/
private boolean enableSecurity = true;

/**
* 密码加密算法
*/
private String passwordEncoder = "BCRYPT";

/**
* 密码最小长度
*/
private int minPasswordLength = 8;

/**
* 密码最大长度
*/
private int maxPasswordLength = 20;

/**
* 登录失败锁定次数
*/
private int maxLoginAttempts = 5;

/**
* 登录失败锁定时间(分钟)
*/
private int lockoutTime = 30;

/**
* 是否启用双因子认证
*/
private boolean enableTwoFactor = false;

/**
* 是否启用设备绑定
*/
private boolean enableDeviceBinding = false;
}

/**
* 监控配置类
*/
@Data
public static class MonitorConfig {
/**
* 是否启用监控
*/
private boolean enableMonitoring = true;

/**
* 监控间隔(毫秒)
*/
private long monitorInterval = 30000;

/**
* 是否启用告警
*/
private boolean enableAlert = true;

/**
* 登录失败率告警阈值
*/
private double loginFailureRateThreshold = 0.1;

/**
* 并发用户数告警阈值
*/
private int concurrentUsersThreshold = 10000;
}
}

2.3 SSO数据模型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
/**
* SSO用户数据模型类
* @author Java实战
*/
@Data
@Table(name = "sso_user")
public class SsoUser {

/**
* 用户ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

/**
* 用户名
*/
@Column(name = "username", length = 50, unique = true)
private String username;

/**
* 密码
*/
@Column(name = "password", length = 100)
private String password;

/**
* 邮箱
*/
@Column(name = "email", length = 100)
private String email;

/**
* 手机号
*/
@Column(name = "phone", length = 20)
private String phone;

/**
* 真实姓名
*/
@Column(name = "real_name", length = 50)
private String realName;

/**
* 用户状态
*/
@Column(name = "status", length = 20)
private String status;

/**
* 用户类型
*/
@Column(name = "user_type", length = 20)
private String userType;

/**
* 部门ID
*/
@Column(name = "department_id")
private Long departmentId;

/**
* 角色列表(JSON格式)
*/
@Column(name = "roles", columnDefinition = "TEXT")
private String roles;

/**
* 权限列表(JSON格式)
*/
@Column(name = "permissions", columnDefinition = "TEXT")
private String permissions;

/**
* 扩展属性(JSON格式)
*/
@Column(name = "extended_properties", columnDefinition = "TEXT")
private String extendedProperties;

/**
* 最后登录时间
*/
@Column(name = "last_login_time")
private LocalDateTime lastLoginTime;

/**
* 最后登录IP
*/
@Column(name = "last_login_ip", length = 50)
private String lastLoginIp;

/**
* 创建时间
*/
@Column(name = "create_time")
@CreationTimestamp
private LocalDateTime createTime;

/**
* 更新时间
*/
@Column(name = "update_time")
@UpdateTimestamp
private LocalDateTime updateTime;

public SsoUser() {
this.status = "ACTIVE";
this.userType = "USER";
}

public SsoUser(String username, String password) {
this();
this.username = username;
this.password = password;
}

/**
* 验证用户数据
* @return 是否有效
*/
public boolean validate() {
if (username == null || username.isEmpty()) {
return false;
}

if (password == null || password.isEmpty()) {
return false;
}

return true;
}

/**
* 是否激活
* @return 是否激活
*/
public boolean isActive() {
return "ACTIVE".equals(this.status);
}

/**
* 是否锁定
* @return 是否锁定
*/
public boolean isLocked() {
return "LOCKED".equals(this.status);
}

/**
* 设置角色列表
* @param roles 角色列表
*/
public void setRoles(List<String> roles) {
this.roles = JSON.toJSONString(roles);
}

/**
* 获取角色列表
* @return 角色列表
*/
public List<String> getRoles() {
if (roles == null || roles.isEmpty()) {
return new ArrayList<>();
}
try {
return JSON.parseObject(roles, new TypeReference<List<String>>() {});
} catch (Exception e) {
return new ArrayList<>();
}
}

/**
* 设置权限列表
* @param permissions 权限列表
*/
public void setPermissions(List<String> permissions) {
this.permissions = JSON.toJSONString(permissions);
}

/**
* 获取权限列表
* @return 权限列表
*/
public List<String> getPermissions() {
if (permissions == null || permissions.isEmpty()) {
return new ArrayList<>();
}
try {
return JSON.parseObject(permissions, new TypeReference<List<String>>() {});
} catch (Exception e) {
return new ArrayList<>();
}
}

/**
* 设置扩展属性
* @param key 键
* @param value 值
*/
public void setExtendedProperty(String key, Object value) {
Map<String, Object> properties = getExtendedPropertiesMap();
properties.put(key, value);
this.extendedProperties = JSON.toJSONString(properties);
}

/**
* 获取扩展属性
* @param key 键
* @return
*/
public Object getExtendedProperty(String key) {
Map<String, Object> properties = getExtendedPropertiesMap();
return properties.get(key);
}

/**
* 获取扩展属性Map
* @return 扩展属性Map
*/
private Map<String, Object> getExtendedPropertiesMap() {
if (extendedProperties == null || extendedProperties.isEmpty()) {
return new HashMap<>();
}
try {
return JSON.parseObject(extendedProperties, new TypeReference<Map<String, Object>>() {});
} catch (Exception e) {
return new HashMap<>();
}
}

/**
* 设置扩展属性Map
* @param properties 扩展属性Map
*/
public void setExtendedPropertiesMap(Map<String, Object> properties) {
this.extendedProperties = JSON.toJSONString(properties);
}
}

/**
* SSO会话数据模型类
* @author Java实战
*/
@Data
@Table(name = "sso_session")
public class SsoSession {

/**
* 会话ID
*/
@Id
@Column(name = "session_id", length = 64)
private String sessionId;

/**
* 用户ID
*/
@Column(name = "user_id")
private Long userId;

/**
* 用户名
*/
@Column(name = "username", length = 50)
private String username;

/**
* 客户端IP
*/
@Column(name = "client_ip", length = 50)
private String clientIp;

/**
* 用户代理
*/
@Column(name = "user_agent", length = 500)
private String userAgent;

/**
* 设备信息
*/
@Column(name = "device_info", length = 200)
private String deviceInfo;

/**
* 会话状态
*/
@Column(name = "session_status", length = 20)
private String sessionStatus;

/**
* 创建时间
*/
@Column(name = "create_time")
private LocalDateTime createTime;

/**
* 最后访问时间
*/
@Column(name = "last_access_time")
private LocalDateTime lastAccessTime;

/**
* 过期时间
*/
@Column(name = "expire_time")
private LocalDateTime expireTime;

/**
* 扩展属性(JSON格式)
*/
@Column(name = "extended_properties", columnDefinition = "TEXT")
private String extendedProperties;

public SsoSession() {
this.sessionStatus = "ACTIVE";
this.createTime = LocalDateTime.now();
this.lastAccessTime = LocalDateTime.now();
}

public SsoSession(String sessionId, Long userId, String username) {
this();
this.sessionId = sessionId;
this.userId = userId;
this.username = username;
}

/**
* 验证会话数据
* @return 是否有效
*/
public boolean validate() {
if (sessionId == null || sessionId.isEmpty()) {
return false;
}

if (userId == null) {
return false;
}

if (username == null || username.isEmpty()) {
return false;
}

return true;
}

/**
* 是否过期
* @return 是否过期
*/
public boolean isExpired() {
if (expireTime == null) {
return false;
}
return LocalDateTime.now().isAfter(expireTime);
}

/**
* 是否活跃
* @return 是否活跃
*/
public boolean isActive() {
return "ACTIVE".equals(this.sessionStatus) && !isExpired();
}

/**
* 更新最后访问时间
*/
public void updateLastAccessTime() {
this.lastAccessTime = LocalDateTime.now();
}

/**
* 设置扩展属性
* @param key 键
* @param value 值
*/
public void setExtendedProperty(String key, Object value) {
Map<String, Object> properties = getExtendedPropertiesMap();
properties.put(key, value);
this.extendedProperties = JSON.toJSONString(properties);
}

/**
* 获取扩展属性
* @param key 键
* @return
*/
public Object getExtendedProperty(String key) {
Map<String, Object> properties = getExtendedPropertiesMap();
return properties.get(key);
}

/**
* 获取扩展属性Map
* @return 扩展属性Map
*/
private Map<String, Object> getExtendedPropertiesMap() {
if (extendedProperties == null || extendedProperties.isEmpty()) {
return new HashMap<>();
}
try {
return JSON.parseObject(extendedProperties, new TypeReference<Map<String, Object>>() {});
} catch (Exception e) {
return new HashMap<>();
}
}

/**
* 设置扩展属性Map
* @param properties 扩展属性Map
*/
public void setExtendedPropertiesMap(Map<String, Object> properties) {
this.extendedProperties = JSON.toJSONString(properties);
}
}

/**
* SSO登录结果类
* @author Java实战
*/
@Data
public class SsoLoginResult {

/**
* 是否成功
*/
private boolean success;

/**
* 用户信息
*/
private SsoUser user;

/**
* 会话信息
*/
private SsoSession session;

/**
* 访问令牌
*/
private String accessToken;

/**
* 刷新令牌
*/
private String refreshToken;

/**
* 令牌过期时间
*/
private LocalDateTime tokenExpireTime;

/**
* 错误信息
*/
private String error;

/**
* 错误代码
*/
private String errorCode;

/**
* 登录时间
*/
private LocalDateTime loginTime;

/**
* 客户端IP
*/
private String clientIp;

/**
* 用户代理
*/
private String userAgent;

/**
* 设备信息
*/
private String deviceInfo;

public SsoLoginResult() {
this.success = false;
this.loginTime = LocalDateTime.now();
}

public SsoLoginResult(SsoUser user, SsoSession session) {
this();
this.user = user;
this.session = session;
this.success = true;
}

/**
* 是否成功
* @return 是否成功
*/
public boolean isSuccess() {
return success;
}

/**
* 设置错误信息
* @param errorCode 错误代码
* @param error 错误信息
*/
public void setError(String errorCode, String error) {
this.errorCode = errorCode;
this.error = error;
this.success = false;
}
}

2.4 基础SSO服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/**
* 基础SSO服务
* @author Java实战
*/
@Service
@Slf4j
public class SsoService {

@Autowired
private SsoProperties properties;

@Autowired
private JwtUtil jwtUtil;

@Autowired
private RedisUtil redisUtil;

@Autowired
private SsoAuthService ssoAuthService;

@Autowired
private SsoAuthorizationService ssoAuthorizationService;

@Autowired
private SsoMonitorService ssoMonitorService;

@Autowired
private SsoUserRepository ssoUserRepository;

@Autowired
private SsoSessionRepository ssoSessionRepository;

/**
* 用户登录
* @param username 用户名
* @param password 密码
* @param clientIp 客户端IP
* @param userAgent 用户代理
* @return 登录结果
*/
public SsoLoginResult login(String username, String password, String clientIp, String userAgent) {
log.info("用户登录,用户名: {}, 客户端IP: {}", username, clientIp);

SsoLoginResult result = new SsoLoginResult();
result.setClientIp(clientIp);
result.setUserAgent(userAgent);

try {
// 参数验证
if (username == null || username.isEmpty()) {
result.setError("INVALID_USERNAME", "用户名不能为空");
return result;
}

if (password == null || password.isEmpty()) {
result.setError("INVALID_PASSWORD", "密码不能为空");
return result;
}

// 用户认证
SsoUser user = ssoAuthService.authenticateUser(username, password);
if (user == null) {
result.setError("AUTHENTICATION_FAILED", "用户名或密码错误");
ssoMonitorService.recordLoginFailure(username, clientIp, "AUTHENTICATION_FAILED");
return result;
}

// 检查用户状态
if (!user.isActive()) {
result.setError("USER_INACTIVE", "用户已被禁用");
ssoMonitorService.recordLoginFailure(username, clientIp, "USER_INACTIVE");
return result;
}

// 检查用户锁定
if (user.isLocked()) {
result.setError("USER_LOCKED", "用户已被锁定");
ssoMonitorService.recordLoginFailure(username, clientIp, "USER_LOCKED");
return result;
}

// 创建会话
SsoSession session = createSession(user, clientIp, userAgent);
if (session == null) {
result.setError("SESSION_CREATE_FAILED", "会话创建失败");
return result;
}

// 生成令牌
String accessToken = jwtUtil.generateAccessToken(user);
String refreshToken = jwtUtil.generateRefreshToken(user);
LocalDateTime tokenExpireTime = LocalDateTime.now().plusSeconds(properties.getJwt().getExpirationTime());

// 保存会话到Redis
saveSessionToRedis(session);

// 更新用户最后登录信息
updateUserLastLogin(user, clientIp);

// 设置登录结果
result.setUser(user);
result.setSession(session);
result.setAccessToken(accessToken);
result.setRefreshToken(refreshToken);
result.setTokenExpireTime(tokenExpireTime);
result.setSuccess(true);

// 记录登录成功
ssoMonitorService.recordLoginSuccess(user, clientIp);

log.info("用户登录成功,用户名: {}, 会话ID: {}", username, session.getSessionId());

return result;

} catch (Exception e) {
log.error("用户登录异常,用户名: {}", username, e);

result.setError("LOGIN_EXCEPTION", "登录异常: " + e.getMessage());
ssoMonitorService.recordLoginFailure(username, clientIp, "LOGIN_EXCEPTION");

return result;
}
}

/**
* 用户登出
* @param sessionId 会话ID
* @return 是否成功
*/
public boolean logout(String sessionId) {
log.info("用户登出,会话ID: {}", sessionId);

try {
if (sessionId == null || sessionId.isEmpty()) {
return false;
}

// 从Redis删除会话
deleteSessionFromRedis(sessionId);

// 更新数据库会话状态
updateSessionStatus(sessionId, "LOGGED_OUT");

// 记录登出日志
ssoMonitorService.recordLogout(sessionId);

log.info("用户登出成功,会话ID: {}", sessionId);

return true;

} catch (Exception e) {
log.error("用户登出异常,会话ID: {}", sessionId, e);
return false;
}
}

/**
* 验证令牌
* @param token 令牌
* @return 用户信息
*/
public SsoUser validateToken(String token) {
log.info("验证令牌");

try {
if (token == null || token.isEmpty()) {
return null;
}

// 验证JWT令牌
Claims claims = jwtUtil.validateToken(token);
if (claims == null) {
return null;
}

// 获取用户ID
Long userId = claims.get("userId", Long.class);
if (userId == null) {
return null;
}

// 查询用户信息
SsoUser user = ssoUserRepository.findById(userId).orElse(null);
if (user == null || !user.isActive()) {
return null;
}

return user;

} catch (Exception e) {
log.error("验证令牌异常", e);
return null;
}
}

/**
* 刷新令牌
* @param refreshToken 刷新令牌
* @return 新的访问令牌
*/
public String refreshToken(String refreshToken) {
log.info("刷新令牌");

try {
if (refreshToken == null || refreshToken.isEmpty()) {
return null;
}

// 验证刷新令牌
Claims claims = jwtUtil.validateToken(refreshToken);
if (claims == null) {
return null;
}

// 获取用户ID
Long userId = claims.get("userId", Long.class);
if (userId == null) {
return null;
}

// 查询用户信息
SsoUser user = ssoUserRepository.findById(userId).orElse(null);
if (user == null || !user.isActive()) {
return null;
}

// 生成新的访问令牌
String newAccessToken = jwtUtil.generateAccessToken(user);

log.info("令牌刷新成功,用户ID: {}", userId);

return newAccessToken;

} catch (Exception e) {
log.error("刷新令牌异常", e);
return null;
}
}

/**
* 检查用户权限
* @param userId 用户ID
* @param permission 权限
* @return 是否有权限
*/
public boolean checkPermission(Long userId, String permission) {
log.info("检查用户权限,用户ID: {}, 权限: {}", userId, permission);

try {
if (userId == null || permission == null || permission.isEmpty()) {
return false;
}

// 查询用户信息
SsoUser user = ssoUserRepository.findById(userId).orElse(null);
if (user == null || !user.isActive()) {
return false;
}

// 检查权限
boolean hasPermission = ssoAuthorizationService.checkPermission(user, permission);

log.info("用户权限检查结果,用户ID: {}, 权限: {}, 结果: {}", userId, permission, hasPermission);

return hasPermission;

} catch (Exception e) {
log.error("检查用户权限异常,用户ID: {}, 权限: {}", userId, permission, e);
return false;
}
}

/**
* 创建会话
* @param user 用户信息
* @param clientIp 客户端IP
* @param userAgent 用户代理
* @return 会话信息
*/
private SsoSession createSession(SsoUser user, String clientIp, String userAgent) {
try {
// 生成会话ID
String sessionId = generateSessionId();

// 创建会话
SsoSession session = new SsoSession(sessionId, user.getId(), user.getUsername());
session.setClientIp(clientIp);
session.setUserAgent(userAgent);
session.setDeviceInfo(extractDeviceInfo(userAgent));

// 设置过期时间
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(properties.getSession().getSessionTimeout());
session.setExpireTime(expireTime);

// 保存会话到数据库
ssoSessionRepository.save(session);

return session;

} catch (Exception e) {
log.error("创建会话异常", e);
return null;
}
}

/**
* 生成会话ID
* @return 会话ID
*/
private String generateSessionId() {
return UUID.randomUUID().toString().replace("-", "");
}

/**
* 提取设备信息
* @param userAgent 用户代理
* @return 设备信息
*/
private String extractDeviceInfo(String userAgent) {
if (userAgent == null || userAgent.isEmpty()) {
return "Unknown";
}

// 简单的设备信息提取
if (userAgent.contains("Mobile")) {
return "Mobile";
} else if (userAgent.contains("Tablet")) {
return "Tablet";
} else {
return "Desktop";
}
}

/**
* 保存会话到Redis
* @param session 会话信息
*/
private void saveSessionToRedis(SsoSession session) {
try {
String key = properties.getSession().getKeyPrefix() + session.getSessionId();
String value = JSON.toJSONString(session);
int expireTime = properties.getSession().getSessionTimeout();

redisUtil.set(key, value, expireTime);

} catch (Exception e) {
log.error("保存会话到Redis异常", e);
}
}

/**
* 从Redis删除会话
* @param sessionId 会话ID
*/
private void deleteSessionFromRedis(String sessionId) {
try {
String key = properties.getSession().getKeyPrefix() + sessionId;
redisUtil.delete(key);

} catch (Exception e) {
log.error("从Redis删除会话异常", e);
}
}

/**
* 更新会话状态
* @param sessionId 会话ID
* @param status 状态
*/
private void updateSessionStatus(String sessionId, String status) {
try {
Optional<SsoSession> sessionOpt = ssoSessionRepository.findById(sessionId);
if (sessionOpt.isPresent()) {
SsoSession session = sessionOpt.get();
session.setSessionStatus(status);
ssoSessionRepository.save(session);
}

} catch (Exception e) {
log.error("更新会话状态异常", e);
}
}

/**
* 更新用户最后登录信息
* @param user 用户信息
* @param clientIp 客户端IP
*/
private void updateUserLastLogin(SsoUser user, String clientIp) {
try {
user.setLastLoginTime(LocalDateTime.now());
user.setLastLoginIp(clientIp);
ssoUserRepository.save(user);

} catch (Exception e) {
log.error("更新用户最后登录信息异常", e);
}
}
}

3. 高级功能实现

3.1 JWT工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**
* JWT工具类
* @author Java实战
*/
@Component
@Slf4j
public class JwtUtil {

private final SsoProperties.JwtConfig jwtConfig;

private final SecretKey secretKey;

private final JwtParser jwtParser;

public JwtUtil(SsoProperties.JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
this.secretKey = Keys.hmacShaKeyFor(jwtConfig.getSecretKey().getBytes());
this.jwtParser = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build();
}

/**
* 生成访问令牌
* @param user 用户信息
* @return 访问令牌
*/
public String generateAccessToken(SsoUser user) {
log.info("生成访问令牌,用户ID: {}", user.getId());

try {
Date now = new Date();
Date expireTime = new Date(now.getTime() + jwtConfig.getExpirationTime() * 1000);

return Jwts.builder()
.setSubject(user.getUsername())
.setIssuer(jwtConfig.getIssuer())
.setAudience(jwtConfig.getAudience())
.setIssuedAt(now)
.setExpiration(expireTime)
.claim("userId", user.getId())
.claim("username", user.getUsername())
.claim("email", user.getEmail())
.claim("roles", user.getRoles())
.claim("permissions", user.getPermissions())
.claim("tokenType", "ACCESS")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();

} catch (Exception e) {
log.error("生成访问令牌异常", e);
return null;
}
}

/**
* 生成刷新令牌
* @param user 用户信息
* @return 刷新令牌
*/
public String generateRefreshToken(SsoUser user) {
log.info("生成刷新令牌,用户ID: {}", user.getId());

try {
Date now = new Date();
Date expireTime = new Date(now.getTime() + jwtConfig.getRefreshTime() * 1000);

return Jwts.builder()
.setSubject(user.getUsername())
.setIssuer(jwtConfig.getIssuer())
.setAudience(jwtConfig.getAudience())
.setIssuedAt(now)
.setExpiration(expireTime)
.claim("userId", user.getId())
.claim("username", user.getUsername())
.claim("tokenType", "REFRESH")
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();

} catch (Exception e) {
log.error("生成刷新令牌异常", e);
return null;
}
}

/**
* 验证令牌
* @param token 令牌
* @return 令牌声明
*/
public Claims validateToken(String token) {
log.info("验证令牌");

try {
if (token == null || token.isEmpty()) {
return null;
}

Claims claims = jwtParser.parseClaimsJws(token).getBody();

// 检查令牌类型
String tokenType = claims.get("tokenType", String.class);
if (tokenType == null) {
return null;
}

// 检查过期时间
if (claims.getExpiration().before(new Date())) {
return null;
}

return claims;

} catch (JwtException e) {
log.warn("令牌验证失败", e);
return null;
} catch (Exception e) {
log.error("验证令牌异常", e);
return null;
}
}

/**
* 从令牌中获取用户ID
* @param token 令牌
* @return 用户ID
*/
public Long getUserIdFromToken(String token) {
Claims claims = validateToken(token);
if (claims == null) {
return null;
}
return claims.get("userId", Long.class);
}

/**
* 从令牌中获取用户名
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
Claims claims = validateToken(token);
if (claims == null) {
return null;
}
return claims.get("username", String.class);
}

/**
* 从令牌中获取角色列表
* @param token 令牌
* @return 角色列表
*/
@SuppressWarnings("unchecked")
public List<String> getRolesFromToken(String token) {
Claims claims = validateToken(token);
if (claims == null) {
return new ArrayList<>();
}
return claims.get("roles", List.class);
}

/**
* 从令牌中获取权限列表
* @param token 令牌
* @return 权限列表
*/
@SuppressWarnings("unchecked")
public List<String> getPermissionsFromToken(String token) {
Claims claims = validateToken(token);
if (claims == null) {
return new ArrayList<>();
}
return claims.get("permissions", List.class);
}

/**
* 检查令牌是否过期
* @param token 令牌
* @return 是否过期
*/
public boolean isTokenExpired(String token) {
Claims claims = validateToken(token);
if (claims == null) {
return true;
}
return claims.getExpiration().before(new Date());
}

/**
* 获取令牌过期时间
* @param token 令牌
* @return 过期时间
*/
public Date getTokenExpiration(String token) {
Claims claims = validateToken(token);
if (claims == null) {
return null;
}
return claims.getExpiration();
}

/**
* 获取配置信息
* @return 配置信息
*/
public Map<String, Object> getConfig() {
Map<String, Object> config = new HashMap<>();
config.put("secretKey", jwtConfig.getSecretKey());
config.put("expirationTime", jwtConfig.getExpirationTime());
config.put("refreshTime", jwtConfig.getRefreshTime());
config.put("issuer", jwtConfig.getIssuer());
config.put("audience", jwtConfig.getAudience());
config.put("algorithm", jwtConfig.getAlgorithm());
return config;
}
}

3.2 Redis工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* Redis工具类
* @author Java实战
*/
@Component
@Slf4j
public class RedisUtil {

private final SsoProperties.RedisConfig redisConfig;

private final RedisTemplate<String, Object> redisTemplate;

public RedisUtil(SsoProperties.RedisConfig redisConfig) {
this.redisConfig = redisConfig;
this.redisTemplate = createRedisTemplate();
}

/**
* 创建Redis模板
* @return Redis模板
*/
private RedisTemplate<String, Object> createRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();

// 连接工厂
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(
new RedisStandaloneConfiguration(redisConfig.getHost(), redisConfig.getPort())
);
connectionFactory.setPassword(redisConfig.getPassword());
connectionFactory.setDatabase(redisConfig.getDatabase());
connectionFactory.setTimeout(Duration.ofMillis(redisConfig.getConnectionTimeout()));

template.setConnectionFactory(connectionFactory);

// 序列化配置
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();

return template;
}

/**
* 设置键值
* @param key 键
* @param value 值
*/
public void set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
} catch (Exception e) {
log.error("设置Redis键值异常,键: {}", key, e);
}
}

/**
* 设置键值(带过期时间)
* @param key 键
* @param value 值
* @param expireTime 过期时间(秒)
*/
public void set(String key, Object value, int expireTime) {
try {
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(expireTime));
} catch (Exception e) {
log.error("设置Redis键值异常,键: {}, 过期时间: {}", key, expireTime, e);
}
}

/**
* 获取值
* @param key 键
* @return
*/
public Object get(String key) {
try {
return redisTemplate.opsForValue().get(key);
} catch (Exception e) {
log.error("获取Redis值异常,键: {}", key, e);
return null;
}
}

/**
* 删除键
* @param key 键
* @return 是否成功
*/
public boolean delete(String key) {
try {
return Boolean.TRUE.equals(redisTemplate.delete(key));
} catch (Exception e) {
log.error("删除Redis键异常,键: {}", key, e);
return false;
}
}

/**
* 检查键是否存在
* @param key 键
* @return 是否存在
*/
public boolean exists(String key) {
try {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
} catch (Exception e) {
log.error("检查Redis键是否存在异常,键: {}", key, e);
return false;
}
}

/**
* 设置过期时间
* @param key 键
* @param expireTime 过期时间(秒)
* @return 是否成功
*/
public boolean expire(String key, int expireTime) {
try {
return Boolean.TRUE.equals(redisTemplate.expire(key, Duration.ofSeconds(expireTime)));
} catch (Exception e) {
log.error("设置Redis键过期时间异常,键: {}, 过期时间: {}", key, expireTime, e);
return false;
}
}

/**
* 获取过期时间
* @param key 键
* @return 过期时间(秒)
*/
public long getExpire(String key) {
try {
Long expire = redisTemplate.getExpire(key);
return expire != null ? expire : -1;
} catch (Exception e) {
log.error("获取Redis键过期时间异常,键: {}", key, e);
return -1;
}
}

/**
* 批量删除键
* @param keys 键列表
* @return 删除数量
*/
public long delete(String... keys) {
try {
return redisTemplate.delete(Arrays.asList(keys));
} catch (Exception e) {
log.error("批量删除Redis键异常", e);
return 0;
}
}

/**
* 获取配置信息
* @return 配置信息
*/
public Map<String, Object> getConfig() {
Map<String, Object> config = new HashMap<>();
config.put("host", redisConfig.getHost());
config.put("port", redisConfig.getPort());
config.put("database", redisConfig.getDatabase());
config.put("connectionTimeout", redisConfig.getConnectionTimeout());
config.put("readTimeout", redisConfig.getReadTimeout());
config.put("maxConnections", redisConfig.getMaxConnections());
config.put("minIdleConnections", redisConfig.getMinIdleConnections());
return config;
}
}

4. SSO控制器

4.1 SSO REST控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
* SSO REST控制器
* @author Java实战
*/
@RestController
@RequestMapping("/api/sso")
@Slf4j
public class SsoController {

@Autowired
private SsoService ssoService;

@Autowired
private SsoMonitorService ssoMonitorService;

/**
* 用户登录
* @param request 登录请求
* @return 登录结果
*/
@PostMapping("/login")
public ResponseEntity<SsoLoginResult> login(@RequestBody SsoLoginRequest request, HttpServletRequest httpRequest) {
try {
log.info("接收到用户登录请求,用户名: {}", request.getUsername());

// 参数验证
if (request.getUsername() == null || request.getUsername().isEmpty()) {
return ResponseEntity.badRequest().build();
}

if (request.getPassword() == null || request.getPassword().isEmpty()) {
return ResponseEntity.badRequest().build();
}

// 获取客户端信息
String clientIp = getClientIp(httpRequest);
String userAgent = httpRequest.getHeader("User-Agent");

// 执行登录
SsoLoginResult result = ssoService.login(
request.getUsername(),
request.getPassword(),
clientIp,
userAgent
);

if (result.isSuccess()) {
return ResponseEntity.ok(result);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(result);
}

} catch (Exception e) {
log.error("用户登录失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 用户登出
* @param sessionId 会话ID
* @return 是否成功
*/
@PostMapping("/logout")
public ResponseEntity<Boolean> logout(@RequestParam String sessionId) {
try {
log.info("接收到用户登出请求,会话ID: {}", sessionId);

if (sessionId == null || sessionId.isEmpty()) {
return ResponseEntity.badRequest().build();
}

boolean success = ssoService.logout(sessionId);

return ResponseEntity.ok(success);

} catch (Exception e) {
log.error("用户登出失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 验证令牌
* @param token 令牌
* @return 用户信息
*/
@GetMapping("/validate")
public ResponseEntity<SsoUser> validateToken(@RequestParam String token) {
try {
log.info("接收到令牌验证请求");

if (token == null || token.isEmpty()) {
return ResponseEntity.badRequest().build();
}

SsoUser user = ssoService.validateToken(token);

if (user != null) {
return ResponseEntity.ok(user);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

} catch (Exception e) {
log.error("令牌验证失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 刷新令牌
* @param refreshToken 刷新令牌
* @return 新的访问令牌
*/
@PostMapping("/refresh")
public ResponseEntity<String> refreshToken(@RequestParam String refreshToken) {
try {
log.info("接收到令牌刷新请求");

if (refreshToken == null || refreshToken.isEmpty()) {
return ResponseEntity.badRequest().build();
}

String newAccessToken = ssoService.refreshToken(refreshToken);

if (newAccessToken != null) {
return ResponseEntity.ok(newAccessToken);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

} catch (Exception e) {
log.error("令牌刷新失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 检查用户权限
* @param userId 用户ID
* @param permission 权限
* @return 是否有权限
*/
@GetMapping("/permission")
public ResponseEntity<Boolean> checkPermission(
@RequestParam Long userId,
@RequestParam String permission) {
try {
log.info("接收到权限检查请求,用户ID: {}, 权限: {}", userId, permission);

if (userId == null || permission == null || permission.isEmpty()) {
return ResponseEntity.badRequest().build();
}

boolean hasPermission = ssoService.checkPermission(userId, permission);

return ResponseEntity.ok(hasPermission);

} catch (Exception e) {
log.error("权限检查失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取SSO状态
* @return SSO状态
*/
@GetMapping("/status")
public ResponseEntity<SsoStatus> getSsoStatus() {
try {
log.info("接收到获取SSO状态请求");

SsoStatus status = ssoMonitorService.getSsoStatus();

return ResponseEntity.ok(status);

} catch (Exception e) {
log.error("获取SSO状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 健康检查
* @return 健康状态
*/
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
try {
Map<String, Object> health = new HashMap<>();

SsoStatus status = ssoMonitorService.getSsoStatus();

health.put("status", status != null && status.isHealthy() ? "UP" : "DOWN");
health.put("timestamp", System.currentTimeMillis());
health.put("details", status);

return ResponseEntity.ok(health);

} catch (Exception e) {
log.error("健康检查失败", e);

Map<String, Object> health = new HashMap<>();
health.put("status", "DOWN");
health.put("timestamp", System.currentTimeMillis());
health.put("error", e.getMessage());

return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(health);
}
}

/**
* 获取客户端IP
* @param request HTTP请求
* @return 客户端IP
*/
private String getClientIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}

String xRealIp = request.getHeader("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty()) {
return xRealIp;
}

return request.getRemoteAddr();
}
}

/**
* SSO登录请求类
* @author Java实战
*/
@Data
public class SsoLoginRequest {

/**
* 用户名
*/
@NotBlank(message = "用户名不能为空")
private String username;

/**
* 密码
*/
@NotBlank(message = "密码不能为空")
private String password;

/**
* 记住我
*/
private boolean rememberMe = false;

/**
* 验证码
*/
private String captcha;

/**
* 设备信息
*/
private String deviceInfo;
}

5. 总结

5.1 单点登录SSO最佳实践

  1. 合理选择认证方案: 根据业务需求选择合适的认证方案
  2. 安全性设计: 确保认证和授权的安全性
  3. 性能优化: 优化认证和授权性能
  4. 监控告警: 实时监控SSO系统状态
  5. 用户体验: 提供良好的用户体验
  6. 系统集成: 简化系统间的集成
  7. 合规性: 满足企业安全合规要求
  8. 审计追踪: 完整的用户行为审计

5.2 性能优化建议

  • 缓存策略: 合理使用缓存减少数据库访问
  • 令牌优化: 优化JWT令牌的生成和验证
  • 会话管理: 优化会话存储和管理
  • 连接池优化: 优化数据库和Redis连接池
  • 异步处理: 异步处理非关键路径
  • 负载均衡: 合理分配SSO服务负载

5.3 运维管理要点

  • 实时监控: 监控SSO系统状态和性能
  • 故障处理: 建立完善的故障处理机制
  • 性能调优: 根据监控数据优化性能
  • 日志管理: 完善日志记录和分析
  • 容量规划: 合理规划SSO服务容量
  • 安全管理: 确保SSO系统安全
  • 用户管理: 完善用户和权限管理
  • 合规管理: 确保符合安全合规要求

通过本文的单点登录SSO Java实战指南,您可以掌握企业级单点登录解决方案的原理、实现细节、性能优化技巧以及在企业级应用中的最佳实践,构建高效、可靠、安全的单点登录系统!