一、SSO的本质价值与实现模型
在分布式系统架构中,单点登录(Single Sign-On)如同企业级应用的"万能钥匙",用户只需在统一认证中心完成一次身份验证,即可访问所有互信系统。其核心在于建立可信的令牌传递机制,我们选择JWT(JSON Web Token)作为载体,结合OAuth2简化模式实现轻量级SSO方案。
关键技术原理:
认证中心统一鉴权:独立部署的认证服务负责用户身份核验
令牌传播机制:使用数字签名的JWT保障令牌安全传输
跨域会话管理:通过Cookie+LocalStorage实现跨域状态同步
(图源网络,侵权删除)
二、前端Vue实现方案
1. 核心依赖配置
npm install axios vue-router js-cookie
2. 路由守卫实现鉴权
// router/index.js
router.beforeEach((to, from, next) => {
const token = Cookies.get('SSO_TOKEN');
if (to.meta.requiresAuth) {
if (!token) {
// 重定向到认证中心
window.location.href = `https://auth-center.com/login?redirect=${encodeURIComponent(window.location.href)}`;
} else {
// 验证令牌有效性
axios.get('/api/verify_token', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(() => next()).catch(() => {
Cookies.remove('SSO_TOKEN');
next('/login');
});
}
} else {
next();
}
});
3. 令牌回调处理
// LoginCallback.vue
export default {
mounted() {
const token = this.$route.query.token;
if (token) {
Cookies.set('SSO_TOKEN', token, { expires: 1 });
const redirect = decodeURIComponent(this.$route.query.redirect || '/');
this.$router.push(redirect);
}
}
}
三、后端Java实现(Spring Boot)
1. Maven依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. JWT工具类
public class JwtUtil {
private static final String SECRET_KEY = "your-256-bit-secret";
private static final long EXPIRATION = 3600L * 1000; // 1小时
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY.getBytes())
.build()
.parseClaimsJws(token)
.getBody();
}
}
3. 认证中心接口
@RestController
@RequestMapping("/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// 1. 执行用户名密码验证
User user = userService.authenticate(request);
// 2. 生成JWT令牌
String token = JwtUtil.generateToken(user.getUsername());
// 3. 返回带跳转地址的响应
return ResponseEntity.ok(Map.of(
"token", token,
"redirect", request.getRedirectUrl()
));
}
}
4. 业务系统鉴权拦截器
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (StringUtils.isEmpty(token)) {
throw new AuthException("Missing authentication token");
}
try {
Claims claims = JwtUtil.parseToken(token.replace("Bearer ", ""));
request.setAttribute("username", claims.getSubject());
return true;
} catch (JwtException e) {
throw new AuthException("Invalid token");
}
}
}
四、安全增强策略
HTTPS强制传输:全链路启用SSL加密
双Token机制:AccessToken(短时效) + RefreshToken(长时效)
跨域安全配置:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://your-domain.com")
.allowCredentials(true)
.allowedMethods("GET", "POST");
}
}
五、典型问题排查指南
现象 | 排查方向 | 解决方案 |
---|---|---|
跨域请求失败 | CORS配置、Cookie作用域 | 检查Origin头、SameSite设置 |
Token无法解析 | 密钥一致性、编码格式 | 验证HS256算法和Base64编码 |
会话状态不同步 | 域名协议一致性、存储方式 | 确保二级域名相同,检查LocalStorage |