首页
/ 【实测封神】Smart-SSO:5分钟搭建企业级单点登录系统,彻底终结多系统账号混乱难题

【实测封神】Smart-SSO:5分钟搭建企业级单点登录系统,彻底终结多系统账号混乱难题

2026-02-04 04:29:12作者:俞予舒Fleming

你是否还在为企业内部10+系统重复登录而抓狂?用户抱怨密码记不住,IT部门疲于账号管理,安全审计更是一团乱麻?今天给大家带来的Smart-SSO开源项目,基于SpringBoot+OAuth2打造,开箱即用的轻量级单点登录解决方案,完美解决跨系统认证难题。读完本文你将掌握:

  • 3分钟完成SSO服务端部署的实操步骤
  • 5行代码实现应用系统接入单点登录
  • 分布式环境下的Token共享方案设计
  • 前后端分离架构的权限控制最佳实践
  • 从0到1搭建企业级认证中心的完整指南

一、为什么选择Smart-SSO?市面方案深度横评

企业级单点登录(Single Sign-On,SSO)方案选型一直是架构师的头疼事。我们对当前主流实现方案进行了全方位对比:

特性 传统Session共享 JWT令牌 OAuth2.0标准 Smart-SSO(OAuth2增强版)
单点登录 ✅ 支持 ✅ 支持 ✅ 支持 ✅ 支持
单点退出 ✅ 需额外开发 ❌ 难以实现 ✅ 原生支持 ✅ 一键退出所有系统
踢人下线 ✅ 需额外开发 ❌ 无法实时失效 ✅ 需扩展实现 ✅ 管理员实时管控
过期自动续签 ❌ 依赖Cookie ❌ 需双Token改造 ✅ 原生支持 ✅ 无感自动续签
跨域支持 ❌ 受浏览器限制 ✅ 支持 ✅ 支持 ✅ 全场景跨域解决方案
前后端分离 ❌ 不适用 ✅ 支持 ✅ 支持 ✅ 无Cookie模式优化
按钮级权限 ✅ 需定制开发 ❌ 难以实现 ❌ 需扩展实现 ✅ 原生支持API级控制
分布式部署 ❌ 集群依赖 ✅ 支持 ✅ 需共享存储 ✅ Redis无缝集群
性能损耗 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 本地缓存优化
接入复杂度 ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ 5行代码快速接入

Smart-SSO核心优势在于:基于OAuth2.0标准协议,却优化了其在企业内部系统的使用痛点。通过客户端本地Token缓存减少80%的服务端请求,独创的"双Token+Redis存根"机制,既保留了JWT的无状态优势,又实现了类似Session的实时管控能力。

二、架构解析:Smart-SSO的底层实现原理

2.1 核心组件架构

Smart-SSO采用微服务架构设计,主要包含四大模块:

classDiagram
    class 服务端模块 {
        +认证授权中心
        +用户权限管理
        +Token生命周期管理
        +客户端应用管理
    }
    
    class 客户端模块 {
        +登录过滤器
        +权限过滤器
        +Token本地缓存
        +自动续签组件
    }
    
    class 存储模块 {
        +Redis分布式存储
        +本地内存缓存
        +数据库持久化
    }
    
    class 扩展模块 {
        +前后端分离支持
        +跨域解决方案
        +按钮级权限控制
    }
    
    服务端模块 <--> 客户端模块 : OAuth2.0协议通信
    服务端模块 <--> 存储模块 : Token/权限数据
    客户端模块 <--> 存储模块 : 本地缓存
    服务端模块 <--> 扩展模块 : 功能扩展点

2.2 单点登录流程(OAuth2.0授权码模式增强版)

sequenceDiagram
    participant 用户
    participant 客户端应用
    participant SSO服务端
    participant Redis存储
    
    用户->>客户端应用: 访问受保护资源
    客户端应用->>用户: 重定向到SSO登录页
    用户->>SSO服务端: 输入账号密码登录
    SSO服务端->>SSO服务端: 验证用户身份
    SSO服务端->>客户端应用: 返回授权码(code)
    客户端应用->>SSO服务端: 使用code+ClientSecret换Token
    SSO服务端->>Redis存储: 生成并存储Token
    SSO服务端->>客户端应用: 返回accessToken+refreshToken
    客户端应用->>Redis存储: 缓存Token
    客户端应用->>用户: 登录成功,跳转受保护资源

2.3 单点退出创新实现

传统OAuth2.0协议在单点退出场景存在短板,Smart-SSO创新性地设计了"注销通知网络":

flowchart TD
    A[用户在客户端A点击退出] --> B[客户端A清除本地Token]
    B --> C[调用SSO服务端注销接口]
    C --> D[SSO服务端吊销全局会话]
    D --> E[查询该用户关联的所有客户端]
    E --> F[向所有客户端发送注销通知]
    F --> G[各客户端清除本地会话]
    G --> H[用户在所有系统同时退出]

三、5分钟极速体验:从0到1搭建SSO服务

3.1 环境准备清单

依赖项 版本要求 说明
JDK 17+ master分支要求,1.7分支支持JDK8
Maven 3.6+ 项目构建工具
MySQL 8.0+ 存储用户、权限等核心数据
Redis(可选) 5.0+ 分布式部署必选
Git 2.0+ 代码拉取工具

3.2 服务端部署步骤

# 1. 克隆代码仓库
git clone https://gitcode.com/openjoe/smart-sso.git
cd smart-sso

# 2. 初始化数据库
# 导入db/smart-sso.sql到MySQL
# 修改smart-sso-server/src/main/resources/application.yml中的数据库配置

# 3. 构建项目
mvn clean package -Dmaven.test.skip=true

# 4. 启动服务端
java -jar smart-sso-server/target/smart-sso-server.jar

服务启动成功后,访问 http://localhost:8080 即可看到登录界面,默认管理员账号:admin/123456。

3.3 客户端快速接入(以SpringBoot应用为例)

第一步:添加依赖

<dependency>
    <groupId>openjoe.smart.sso</groupId>
    <artifactId>smart-sso-starter-client</artifactId>
    <version>最新版本</version>
</dependency>

第二步:配置application.yml

smart:
  sso:
    server-url: http://localhost:8080  # SSO服务端地址
    client-id: client1                 # 在SSO服务端注册的客户端ID
    client-secret: secret1             # 客户端密钥
    redirect-uri: http://localhost:8081/client/login  # 回调地址
    logout-uri: http://localhost:8081/client/logout   # 退出地址

第三步:添加注解

@SpringBootApplication
@EnableSSOClient  // 启用SSO客户端
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
}

第四步:配置受保护资源

@RestController
@RequestMapping("/api")
public class ResourceController {
    
    @GetMapping("/userinfo")
    @RequiresPermissions("user:view")  // 权限控制注解
    public Object getUserInfo() {
        // 获取当前登录用户信息
        return SSOUtils.getCurrentUser();
    }
}

第五步:启动客户端应用

java -jar client-app.jar --server.port=8081

访问 http://localhost:8081/api/userinfo 会自动跳转到SSO登录页,登录成功后返回用户信息,整个接入过程不到5行核心代码!

四、高级特性实战:解决企业级复杂场景

4.1 分布式部署方案

当企业用户量增长到一定规模,单节点SSO服务面临性能瓶颈,Smart-SSO提供完整的分布式解决方案:

# 服务端Redis配置
spring:
  redis:
    host: 192.168.1.100
    port: 6379
    password: redis_password
    
smart:
  sso:
    server:
      token-store: redis  # 切换为Redis存储Token
      redis-prefix: "smart-sso:"  # Redis键前缀

客户端同样配置Redis后,多实例部署时Token自动共享,完美支持横向扩展。

4.2 前后端分离架构适配

针对Vue/React等前端框架,Smart-SSO提供无Cookie模式:

// 前端登录示例代码
async function login(username, password) {
  // 1. 获取授权码
  const code = await fetch(`${ssoServer}/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code`);
  
  // 2. 换取Token
  const tokenResponse = await fetch(`${ssoServer}/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `client_id=${clientId}&client_secret=${clientSecret}&code=${code}&grant_type=authorization_code`
  });
  
  const { access_token } = await tokenResponse.json();
  
  // 3. 存储Token到localStorage
  localStorage.setItem('access_token', access_token);
}

// 发起API请求时携带Token
axios.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${localStorage.getItem('access_token')}`;
  return config;
});

4.3 按钮级权限控制实现

Smart-SSO创新性地将权限分为菜单权限和按钮权限两类:

// 后端权限定义示例
@Entity
public class Permission {
    private Long id;
    private String name;         // 权限名称
    private String permission;   // 权限标识(如:user:view)
    private Integer type;        // 1-菜单 2-按钮
    private String uri;          // 请求路径
    private String method;       // 请求方法(GET/POST等)
    // getter/setter省略
}

前端通过获取当前用户权限集合,动态渲染按钮:

<!-- Vue按钮权限控制示例 -->
<template>
  <button v-if="hasPermission('user:add')" @click="addUser">新增用户</button>
  <button v-if="hasPermission('user:edit')" @click="editUser">编辑用户</button>
  <button v-if="hasPermission('user:delete')" @click="deleteUser">删除用户</button>
</template>

<script>
export default {
  methods: {
    hasPermission(permission) {
      return this.$store.state.permissions.includes(permission);
    }
  }
};
</script>

五、生产环境部署最佳实践

5.1 高可用架构设计

flowchart TD
    Client[用户/客户端应用] --> LB[负载均衡器]
    LB --> SSO1[SSO服务端实例1]
    LB --> SSO2[SSO服务端实例2]
    SSO1 <--> Redis[Redis集群]
    SSO2 <--> Redis
    SSO1 <--> DB[MySQL主从]
    SSO2 <--> DB

5.2 安全加固措施

  1. 传输安全:所有通信采用HTTPS加密
  2. 密码安全:使用BCrypt算法加密存储密码
  3. Token安全
    • accessToken有效期设为2小时
    • refreshToken有效期设为7天
    • 敏感操作需二次验证
  4. 防CSRF攻击:实现CSRF Token验证
  5. 接口限流:登录接口限制每分钟5次尝试

5.3 性能优化策略

  1. 客户端本地缓存:减少80%的服务端校验请求
  2. Redis连接池优化
    spring:
      redis:
        lettuce:
          pool:
            max-active: 8
            max-idle: 8
            min-idle: 2
            max-wait: 100ms
    
  3. 数据库索引优化:关键查询字段建立索引
  4. 接口响应压缩:启用Gzip压缩

六、常见问题解决方案

6.1 跨域问题排查指南

当遇到跨域问题时,可按以下步骤排查:

  1. 检查服务端是否配置CorsFilter:
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*");  // 生产环境应限制具体域名
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}
  1. 验证客户端应用配置的redirect-uri是否在SSO服务端白名单中
  2. 检查浏览器控制台Network标签,查看预检请求(OPTIONS)是否通过

6.2 Token续签失败处理

自动续签失败通常有以下原因:

  1. refreshToken已过期:此时需引导用户重新登录
  2. Redis连接异常:检查Redis集群状态
  3. 客户端时间与服务端不同步:确保服务器时间同步

客户端应实现自动续签失败的降级处理:

// Token自动续签实现
function startTokenRefresh() {
  // 在accessToken过期前30分钟开始续签
  const refreshInterval = (getTokenExpiry() - 30 * 60 * 1000) - Date.now();
  
  setTimeout(async () => {
    try {
      const response = await fetch(`${ssoServer}/token`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `grant_type=refresh_token&refresh_token=${localStorage.getItem('refresh_token')}`
      });
      
      if (!response.ok) throw new Error('续签失败');
      
      const { access_token, refresh_token } = await response.json();
      localStorage.setItem('access_token', access_token);
      localStorage.setItem('refresh_token', refresh_token);
      
      // 继续设置下一次续签
      startTokenRefresh();
    } catch (error) {
      // 续签失败,引导重新登录
      localStorage.removeItem('access_token');
      localStorage.removeItem('refresh_token');
      window.location.href = '/login';
    }
  }, refreshInterval);
}

七、项目架构与源码解析

7.1 项目目录结构

smart-sso/
├── LICENSE                  # 开源许可证
├── README.md                # 项目说明文档
├── db/                      # 数据库脚本
│   └── smart-sso.sql        # 初始化SQL脚本
├── smart-sso-demo/          # 客户端示例(传统应用)
├── smart-sso-demo-h5/       # 客户端示例(前后端分离)
├── smart-sso-server/        # 服务端核心模块
│   ├── src/main/java/openjoe/smart/sso/server/
│   │   ├── controller/      # 控制器层
│   │   ├── entity/          # 实体类
│   │   ├── service/         # 服务层
│   │   ├── manager/        </think></think># 【实测封神】Smart-SSO:5分钟搭建企业级单点登录系统,彻底终结多系统账号混乱难题

你是否还在为企业内部10+系统重复登录而抓狂?用户抱怨密码记不住,IT部门疲于账号管理,安全审计更是一团乱麻?今天给大家带来的Smart-SSO开源项目,基于SpringBoot+OAuth2打造,开箱即用的轻量级单点登录解决方案,完美解决跨系统认证难题。**读完本文你将掌握:**
- 3分钟完成SSO服务端部署的实操步骤
- 5行代码实现应用系统接入单点登录
- 分布式环境下的Token共享方案设计
- 前后端分离架构的权限控制最佳实践
- 从0到1搭建企业级认证中心的完整指南

## 一、为什么选择Smart-SSO?市面方案深度横评

企业级单点登录(Single Sign-On,SSO)方案选型一直是架构师的头疼事。我们对当前主流实现方案进行了全方位对比:

| 特性                | 传统Session共享 | JWT令牌         | OAuth2.0标准     | Smart-SSO(OAuth2增强版) |
|---------------------|----------------|-----------------|------------------|--------------------------|
| **单点登录**        | ✅ 支持         | ✅ 支持          | ✅ 支持           | ✅ 支持                   |
| **单点退出**        | ✅ 需额外开发   | ❌ 难以实现      | ✅ 原生支持       | ✅ 一键退出所有系统       |
| **踢人下线**        | ✅ 需额外开发   | ❌ 无法实时失效  | ✅ 需扩展实现     | ✅ 管理员实时管控         |
| **过期自动续签**    | ❌ 依赖Cookie   | ❌ 需双Token改造 | ✅ 原生支持       | ✅ 无感自动续签           |
| **跨域支持**        | ❌ 受浏览器限制 | ✅ 支持          | ✅ 支持           | ✅ 全场景跨域解决方案     |
| **前后端分离**      | ❌ 不适用       | ✅ 支持          | ✅ 支持           | ✅ 无Cookie模式优化       |
| **按钮级权限**      | ✅ 需定制开发   | ❌ 难以实现      | ❌ 需扩展实现     | ✅ 原生支持API级控制      |
| **分布式部署**      | ❌ 集群依赖     | ✅ 支持          | ✅ 需共享存储     | ✅ Redis无缝集群         |
| **性能损耗**        | ⭐⭐⭐          | ⭐⭐⭐⭐⭐        | ⭐⭐⭐⭐           | ⭐⭐⭐⭐⭐ 本地缓存优化     |
| **接入复杂度**      | ⭐⭐⭐⭐        | ⭐⭐⭐           | ⭐               | ⭐⭐ 5行代码快速接入      |

**Smart-SSO核心优势**在于:基于OAuth2.0标准协议,却优化了其在企业内部系统的使用痛点。通过客户端本地Token缓存减少80%的服务端请求,独创的"双Token+Redis存根"机制,既保留了JWT的无状态优势,又实现了类似Session的实时管控能力。

## 二、架构解析:Smart-SSO的底层实现原理

### 2.1 核心组件架构

Smart-SSO采用微服务架构设计,主要包含四大模块:

```mermaid
classDiagram
    class 服务端模块 {
        +认证授权中心
        +用户权限管理
        +Token生命周期管理
        +客户端应用管理
    }
    
    class 客户端模块 {
        +登录过滤器
        +权限过滤器
        +Token本地缓存
        +自动续签组件
    }
    
    class 存储模块 {
        +Redis分布式存储
        +本地内存缓存
        +数据库持久化
    }
    
    class 扩展模块 {
        +前后端分离支持
        +跨域解决方案
        +按钮级权限控制
    }
    
    服务端模块 <--> 客户端模块 : OAuth2.0协议通信
    服务端模块 <--> 存储模块 : Token/权限数据
    客户端模块 <--> 存储模块 : 本地缓存
    服务端模块 <--> 扩展模块 : 功能扩展点

2.2 单点登录流程(OAuth2.0授权码模式增强版)

sequenceDiagram
    participant 用户
    participant 客户端应用
    participant SSO服务端
    participant Redis存储
    
    用户->>客户端应用: 访问受保护资源
    客户端应用->>用户: 重定向到SSO登录页
    用户->>SSO服务端: 输入账号密码登录
    SSO服务端->>SSO服务端: 验证用户身份
    SSO服务端->>客户端应用: 返回授权码(code)
    客户端应用->>SSO服务端: 使用code+ClientSecret换Token
    SSO服务端->>Redis存储: 生成并存储Token
    SSO服务端->>客户端应用: 返回accessToken+refreshToken
    客户端应用->>Redis存储: 缓存Token
    客户端应用->>用户: 登录成功,跳转受保护资源

2.3 单点退出创新实现

传统OAuth2.0协议在单点退出场景存在短板,Smart-SSO创新性地设计了"注销通知网络":

flowchart TD
    A[用户在客户端A点击退出] --> B[客户端A清除本地Token]
    B --> C[调用SSO服务端注销接口]
    C --> D[SSO服务端吊销全局会话]
    D --> E[查询该用户关联的所有客户端]
    E --> F[向所有客户端发送注销通知]
    F --> G[各客户端清除本地会话]
    G --> H[用户在所有系统同时退出]

三、5分钟极速体验:从0到1搭建SSO服务

3.1 环境准备清单

依赖项 版本要求 说明
JDK 17+ master分支要求,1.7分支支持JDK8
Maven 3.6+ 项目构建工具
MySQL 8.0+ 存储用户、权限等核心数据
Redis(可选) 5.0+ 分布式部署必选
Git 2.0+ 代码拉取工具

3.2 服务端部署步骤

# 1. 克隆代码仓库
git clone https://gitcode.com/openjoe/smart-sso.git
cd smart-sso

# 2. 初始化数据库
# 导入db/smart-sso.sql到MySQL
# 修改smart-sso-server/src/main/resources/application.yml中的数据库配置

# 3. 构建项目
mvn clean package -Dmaven.test.skip=true

# 4. 启动服务端
java -jar smart-sso-server/target/smart-sso-server.jar

服务启动成功后,访问 http://localhost:8080 即可看到登录界面,默认管理员账号:admin/123456。

3.3 客户端快速接入(以SpringBoot应用为例)

第一步:添加依赖

<dependency>
    <groupId>openjoe.smart.sso</groupId>
    <artifactId>smart-sso-starter-client</artifactId>
    <version>最新版本</version>
</dependency>

第二步:配置application.yml

smart:
  sso:
    server-url: http://localhost:8080  # SSO服务端地址
    client-id: client1                 # 在SSO服务端注册的客户端ID
    client-secret: secret1             # 客户端密钥
    redirect-uri: http://localhost:8081/client/login  # 回调地址
    logout-uri: http://localhost:8081/client/logout   # 退出地址

第三步:添加注解

@SpringBootApplication
@EnableSSOClient  // 启用SSO客户端
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
}

第四步:配置受保护资源

@RestController
@RequestMapping("/api")
public class ResourceController {
    
    @GetMapping("/userinfo")
    @RequiresPermissions("user:view")  // 权限控制注解
    public Object getUserInfo() {
        // 获取当前登录用户信息
        return SSOUtils.getCurrentUser();
    }
}

第五步:启动客户端应用

java -jar client-app.jar --server.port=8081

访问 http://localhost:8081/api/userinfo 会自动跳转到SSO登录页,登录成功后返回用户信息,整个接入过程不到5行核心代码!

四、高级特性实战:解决企业级复杂场景

4.1 分布式部署方案

当企业用户量增长到一定规模,单节点SSO服务面临性能瓶颈,Smart-SSO提供完整的分布式解决方案:

# 服务端Redis配置
spring:
  redis:
    host: 192.168.1.100
    port: 6379
    password: redis_password
    
smart:
  sso:
    server:
      token-store: redis  # 切换为Redis存储Token
      redis-prefix: "smart-sso:"  # Redis键前缀

客户端同样配置Redis后,多实例部署时Token自动共享,完美支持横向扩展。

4.2 前后端分离架构适配

针对Vue/React等前端框架,Smart-SSO提供无Cookie模式:

// 前端登录示例代码
async function login(username, password) {
  // 1. 获取授权码
  const code = await fetch(`${ssoServer}/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code`);
  
  // 2. 换取Token
  const tokenResponse = await fetch(`${ssoServer}/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `client_id=${clientId}&client_secret=${clientSecret}&code=${code}&grant_type=authorization_code`
  });
  
  const { access_token } = await tokenResponse.json();
  
  // 3. 存储Token到localStorage
  localStorage.setItem('access_token', access_token);
}

// 发起API请求时携带Token
axios.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${localStorage.getItem('access_token')}`;
  return config;
});

4.3 按钮级权限控制实现

Smart-SSO创新性地将权限分为菜单权限和按钮权限两类:

// 后端权限定义示例
@Entity
public class Permission {
    private Long id;
    private String name;         // 权限名称
    private String permission;   // 权限标识(如:user:view)
    private Integer type;        // 1-菜单 2-按钮
    private String uri;          // 请求路径
    private String method;       // 请求方法(GET/POST等)
    // getter/setter省略
}

前端通过获取当前用户权限集合,动态渲染按钮:

<!-- Vue按钮权限控制示例 -->
<template>
  <button v-if="hasPermission('user:add')" @click="addUser">新增用户</button>
  <button v-if="hasPermission('user:edit')" @click="editUser">编辑用户</button>
  <button v-if="hasPermission('user:delete')" @click="deleteUser">删除用户</button>
</template>

<script>
export default {
  methods: {
    hasPermission(permission) {
      return this.$store.state.permissions.includes(permission);
    }
  }
};
</script>

五、生产环境部署最佳实践

5.1 高可用架构设计

flowchart TD
    Client[用户/客户端应用] --> LB[负载均衡器]
    LB --> SSO1[SSO服务端实例1]
    LB --> SSO2[SSO服务端实例2]
    SSO1 <--> Redis[Redis集群]
    SSO2 <--> Redis
    SSO1 <--> DB[MySQL主从]
    SSO2 <--> DB

5.2 安全加固措施

  1. 传输安全:所有通信采用HTTPS加密
  2. 密码安全:使用BCrypt算法加密存储密码
  3. Token安全
    • accessToken有效期设为2小时
    • refreshToken有效期设为7天
    • 敏感操作需二次验证
  4. 防CSRF攻击:实现CSRF Token验证
  5. 接口限流:登录接口限制每分钟5次尝试

5.3 性能优化策略

  1. 客户端本地缓存:减少80%的服务端校验请求
  2. Redis连接池优化
    spring:
      redis:
        lettuce:
          pool:
            max-active: 8
            max-idle: 8
            min-idle: 2
            max-wait: 100ms
    
  3. 数据库索引优化:关键查询字段建立索引
  4. 接口响应压缩:启用Gzip压缩

六、常见问题解决方案

6.1 跨域问题排查指南

当遇到跨域问题时,可按以下步骤排查:

  1. 检查服务端是否配置CorsFilter:
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*");  // 生产环境应限制具体域名
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}
  1. 验证客户端应用配置的redirect-uri是否在SSO服务端白名单中
  2. 检查浏览器控制台Network标签,查看预检请求(OPTIONS)是否通过

6.2 Token续签失败处理

自动续签失败通常有以下原因:

  1. refreshToken已过期:此时需引导用户重新登录
  2. Redis连接异常:检查Redis集群状态
  3. 客户端时间与服务端不同步:确保服务器时间同步

客户端应实现自动续签失败的降级处理:

// Token自动续签实现
function startTokenRefresh() {
  // 在accessToken过期前30分钟开始续签
  const refreshInterval = (getTokenExpiry() - 30 * 60 * 1000) - Date.now();
  
  setTimeout(async () => {
    try {
      const response = await fetch(`${ssoServer}/token`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `grant_type=refresh_token&refresh_token=${localStorage.getItem('refresh_token')}`
      });
      
      if (!response.ok) throw new Error('续签失败');
      
      const { access_token, refresh_token } = await response.json();
      localStorage.setItem('access_token', access_token);
      localStorage.setItem('refresh_token', refresh_token);
      
      // 继续设置下一次续签
      startTokenRefresh();
    } catch (error) {
      // 续签失败,引导重新登录
      localStorage.removeItem('access_token');
      localStorage.removeItem('refresh_token');
      window.location.href = '/login';
    }
  }, refreshInterval);
}

七、项目架构与源码解析

7.1 项目目录结构

smart-sso/
├── LICENSE                  # 开源许可证
├── README.md                # 项目说明文档
├── db/                      # 数据库脚本
│   └── smart-sso.sql        # 初始化SQL脚本
├── smart-sso-demo/          # 客户端示例(传统应用)
├── smart-sso-demo-h5/       # 客户端示例(前后端分离)
├── smart-sso-server/        # 服务端核心模块
│   ├── src/main/java/openjoe/smart/sso/server/
│   │   ├── controller/      # 控制器层
│   │   ├── entity/          # 实体类
│   │   ├── service/         # 服务层
│   │   ├── manager/         # 业务管理器
│   │   ├── mapper/          # 数据访问层
│   │   ├── dto/             # 数据传输对象
│   │   ├── enums/           # 枚举定义
│   │   ├── util/            # 工具类
│   │   └── stage/           # 基础框架
└── smart-sso-starter/       # 客户端依赖模块
    ├── smart-sso-starter-base/        # 基础组件
    ├── smart-sso-starter-client/      # 客户端核心
    ├── smart-sso-starter-client-redis/ # 客户端Redis支持
    ├── smart-sso-starter-server/      # 服务端核心
    └── smart-sso-starter-server-redis/ # 服务端Redis支持

7.2 核心代码解析:Token管理器

Token管理器是SSO服务端的核心组件,负责Token的生成、验证和吊销:

@Service
public class TokenManager {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // Token过期时间:2小时
    private static final int ACCESS_TOKEN_EXPIRE = 2 * 60 * 60;
    // RefreshToken过期时间:7天
    private static final int REFRESH_TOKEN_EXPIRE = 7 * 24 * 60 * 60;
    
    /**
     * 生成Token
     */
    public Token generateToken(User user, App app) {
        // 生成Access Token
        String accessToken = UUID.randomUUID().toString();
        // 生成Refresh Token
        String refreshToken = UUID.randomUUID().toString();
        
        // 存储Access Token
        String accessTokenKey = "smart-sso:access:" + accessToken;
        redisTemplate.opsForValue().set(accessTokenKey, 
            new TokenContent(user.getId(), app.getId(), user.getUsername()),
            ACCESS_TOKEN_EXPIRE, TimeUnit.SECONDS);
        
        // 存储Refresh Token
        String refreshTokenKey = "smart-sso:refresh:" + refreshToken;
        redisTemplate.opsForValue().set(refreshTokenKey, 
            new RefreshTokenContent(user.getId(), app.getId(), accessToken),
            REFRESH_TOKEN_EXPIRE, TimeUnit.SECONDS);
            
        return new Token(accessToken, refreshToken, ACCESS_TOKEN_EXPIRE);
    }
    
    /**
     * 验证Token
     */
    public TokenContent validateToken(String accessToken) {
        String key = "smart-sso:access:" + accessToken;
        TokenContent content = (TokenContent) redisTemplate.opsForValue().get(key);
        if (content == null) {
            throw new InvalidTokenException("Token已过期或无效");
        }
        // 自动续期:当Token剩余时间不足30分钟时,延长有效期
        Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
        if (expire < 30 * 60) {
            redisTemplate.expire(key, ACCESS_TOKEN_EXPIRE, TimeUnit.SECONDS);
        }
        return content;
    }
    
    /**
     * 吊销用户所有Token
     */
    public void revokeUserTokens(Long userId) {
        Set<String> keys = redisTemplate.keys("smart-sso:access:*");
        for (String key : keys) {
            TokenContent content = (TokenContent) redisTemplate.opsForValue().get(key);
            if (content != null && content.getUserId().equals(userId)) {
                redisTemplate.delete(key);
            }
        }
        // 同时删除相关的RefreshToken
        Set<String> refreshKeys = redisTemplate.keys("smart-sso:refresh:*");
        for (String key : refreshKeys) {
            RefreshTokenContent content = (RefreshTokenContent) redisTemplate.opsForValue().get(key);
            if (content != null && content.getUserId().equals(userId)) {
                redisTemplate.delete(key);
            }
        }
    }
}

八、总结与展望

Smart-SSO作为一款企业级单点登录解决方案,凭借其轻量级设计、丰富功能和易于扩展的特性,已在多家企业的生产环境中得到验证。相比商业SSO产品动辄数十万的授权费用,Smart-SSO完全开源免费,却提供了80%的企业所需功能。

未来 roadmap:

  1. 集成OAuth2.1协议支持
  2. 增加多因素认证(MFA)
  3. 实现与LDAP/Active Directory集成
  4. 开发更丰富的客户端SDK(Python/Go等)

如果你正在为企业寻找一款开箱即用的SSO解决方案,Smart-SSO绝对值得一试。项目源码已托管在Gitcode:https://gitcode.com/openjoe/smart-sso,欢迎Star收藏,也期待你的贡献!

登录后查看全文
热门项目推荐
相关项目推荐