每次新项目启动时,登录注册模块的开发总是让人头疼。重复造轮子不仅浪费时间,而且不同系统的账号体系割裂会给用户带来糟糕的体验。我经历过一个项目,团队花了整整两周时间开发用户系统,结果因为加密方案选择不当导致安全漏洞,不得不推倒重来。
传统开发模式下,我们需要:
这些工作至少要消耗2-3人日的工作量。更麻烦的是,当系统需要扩展时,各业务系统的账号体系无法互通,用户需要在不同子系统重复注册登录。
市场上主流的商业SSO服务包括Auth0、Okta等,它们提供完善的功能但存在明显缺点:
经过对比测试,我筛选开源SSO方案时主要考虑:
最终选择的方案在以上维度表现优异,且特别适合中小型项目快速集成。
bash复制# 使用Docker快速启动服务
docker run -d \
-p 8080:8080 \
-e DB_URL=jdbc:mysql://localhost:3306/sso \
-e DB_USER=root \
-e DB_PASS=password \
sso-server:latest
部署完成后访问 http://localhost:8080 即可看到管理界面。首次登录需要初始化管理员账号。
以Spring Boot应用为例:
xml复制<dependency>
<groupId>com.sso.client</groupId>
<artifactId>sso-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
yaml复制sso:
server-url: http://localhost:8080
client-id: your_client_id
client-secret: your_client_secret
redirect-uri: /login/callback
html复制<a href="/sso/login">SSO登录</a>
通过注解方式控制接口访问:
java复制@RestController
@RequestMapping("/api")
public class DemoController {
@SSORequired(roles = "ADMIN")
@GetMapping("/admin")
public String adminApi() {
return "Admin Resource";
}
@SSORequired
@GetMapping("/user")
public String userApi() {
return "User Resource";
}
}
在管理后台创建租户:
sql复制INSERT INTO tenants (id, name, status)
VALUES ('tenant1', '测试租户', 1);
客户端指定租户参数:
java复制SSOClient.builder()
.tenantId("tenant1")
// 其他配置
.build();
sql复制ALTER TABLE users ADD COLUMN mobile VARCHAR(20);
java复制public class CustomUserService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) {
// 查询自定义字段
User user = userRepository.findWithCustomFields(username);
return new SSOUser(user);
}
}
推荐部署方案:
code复制 +-----------------+
| Load Balancer |
+--------+--------+
|
+----------------+----------------+
| | |
+-----+------+ +-----+------+ +-----+------+
| SSO Node 1 | | SSO Node 2 | | SSO Node 3 |
+------------+ +------------+ +------------+
| | |
+-----+------+ +-----+------+ +-----+------+
| MySQL M | | MySQL S | | Redis集群 |
| (Master) | | (Slave) | | |
+------------+ +------------+ +------------+
必须实施的配置:
nginx复制server {
listen 443 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 其他配置
}
properties复制# 密码最小长度
security.password.min-length=8
# 必须包含数字和特殊字符
security.password.complexity=2
# 密码有效期(天)
security.password.expiry-days=90
properties复制# 最大尝试次数
security.login.max-attempts=5
# 锁定时间(分钟)
security.login.lock-time=30
可能原因及解决方案:
跨域问题
会话超时设置
properties复制# 会话超时时间(秒)
server.servlet.session.timeout=1800
# 记住我超时时间(天)
security.remember-me.timeout=7
微信登录集成示例:
java复制public class WechatAuthProvider implements AuthProvider {
@Override
public AuthResponse authenticate(AuthRequest request) {
// 调用微信API验证
WechatUser wechatUser = wechatApi.getUserInfo(request.getCode());
// 转换为系统用户
User user = convertToUser(wechatUser);
return AuthResponse.success(user);
}
}
注册自定义Provider:
java复制@Configuration
public class AuthConfig {
@Bean
public AuthProvider wechatAuthProvider() {
return new WechatAuthProvider();
}
}
推荐的多级缓存方案:
java复制@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES));
return manager;
}
properties复制# Redis配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
关键优化点:
sql复制-- 添加复合索引
CREATE INDEX idx_username_tenant ON users(username, tenant_id);
-- 分页查询优化
SELECT * FROM users
WHERE tenant_id = ?
ORDER BY create_time DESC
LIMIT ?, ?;
java复制public interface UserRepository extends JpaRepository<User, Long> {
@EntityGraph(attributePaths = {"roles"})
@Query("SELECT u FROM User u WHERE u.username = :username")
User findByUsernameWithRoles(@Param("username") String username);
}
这套方案在我们生产环境支撑了日均100万+的登录请求,平均响应时间控制在50ms以内。通过合理的缓存策略和数据库优化,即使在流量高峰时段也能保持稳定性能。