Vue+ASP.NET Core实现跨系统免登录跳转(含完整代码示例)

Vue+ASP.NET Core实现跨系统免登录跳转(含完整代码示例) VueASP.NET Core实现企业级单点登录系统实战指南想象一下这样的场景你的企业拥有多个业务系统员工每天需要在CRM、OA、ERP等不同平台间频繁切换。每次跳转都要重新输入账号密码不仅效率低下还容易引发密码疲劳导致的安全隐患。这正是单点登录SSO技术要解决的核心痛点——一次认证全网通行。今天我们将深入探讨如何基于Vue和ASP.NET Core构建一套高可用的跨系统免登录方案。不同于基础教程本文聚焦企业级实施中的三大关键挑战安全令牌传递机制、动态路由守卫设计和分布式用户状态同步。无论你是需要快速落地SSO的团队负责人还是希望提升架构能力的中高级开发者这里都有值得借鉴的实战经验。1. 单点登录架构设计原理企业级SSO系统的核心在于建立可信令牌交换体系。与简单的Cookie共享不同现代SSO方案需要满足以下安全要求最小权限原则每个系统只能获取必要用户信息时效性控制令牌需设置合理有效期防重放攻击令牌一次性使用或配合nonce机制跨域安全严格限制可信域名白名单典型的令牌交换流程如下图所示以A系统跳转B系统为例sequenceDiagram participant A as 系统A participant B as 系统B participant SSO as 认证中心 A-SSO: 1. 用户登录A系统 SSO-A: 2. 返回A系统本地token A-B: 3. 携带用户标识请求B系统token B-SSO: 4. 验证用户权限 SSO-B: 5. 签发B系统专用token B-A: 6. 返回B系统token A-B: 7. 跳转B系统携带token B-SSO: 8. 验证token有效性 SSO-B: 9. 返回用户会话数据注意实际实现中应避免使用明文传输token推荐采用PKCE(Proof Key for Code Exchange)等安全增强方案2. ASP.NET Core认证服务实现2.1 JWT令牌签发端点在ASP.NET Core中创建安全的令牌签发服务[ApiController] [Route(api/auth)] public class AuthController : ControllerBase { private readonly JwtSettings _jwtSettings; private readonly IUserService _userService; public AuthController(IOptionsJwtSettings jwtSettings, IUserService userService) { _jwtSettings jwtSettings.Value; _userService userService; } [HttpPost(token)] public async TaskIActionResult GenerateCrossSystemToken([FromBody] TokenRequest request) { // 验证源系统身份 var isValidSource await _userService.ValidateSystemSecret( request.SourceSystemId, request.SourceSystemSecret); if (!isValidSource) return Unauthorized(); // 获取目标系统配置 var targetSystem await _userService .GetSystemConfig(request.TargetSystemId); // 生成限定范围的令牌 var token JwtHelper.GenerateToken(new UserClaims { UserId request.UserId, System request.TargetSystemId, Roles await _userService.GetUserRoles(request.UserId), Permissions await _userService.GetUserPermissions(request.UserId) }, _jwtSettings, targetSystem.TokenLifetime); // 记录令牌签发日志 await _userService.LogTokenIssue( request.UserId, request.SourceSystemId, request.TargetSystemId, token.ExpiresAt); return Ok(new { token token.AccessToken, expires_in (int)(token.ExpiresAt - DateTime.UtcNow).TotalSeconds }); } }关键安全措施双因素系统认证要求源系统提供IDSecret组合动态有效期根据不同系统安全级别设置不同token寿命签发审计记录所有跨系统令牌签发记录2.2 令牌验证中间件创建自定义的令牌验证管道public class CrossSystemTokenMiddleware { private readonly RequestDelegate _next; private readonly JwtSettings _jwtSettings; public CrossSystemTokenMiddleware( RequestDelegate next, IOptionsJwtSettings jwtSettings) { _next next; _jwtSettings jwtSettings.Value; } public async Task InvokeAsync(HttpContext context) { // 跳过登录相关路由 if (context.Request.Path.StartsWithSegments(/api/auth)) { await _next(context); return; } // 从查询字符串获取token适应前端跳转场景 var token context.Request.Query[access_token].FirstOrDefault() ?? context.Request.Headers[Authorization].ToString().Split( ).Last(); if (!string.IsNullOrEmpty(token)) { try { var principal JwtHelper.ValidateToken(token, _jwtSettings); context.User principal; // 检查令牌是否被撤销 var tokenRevoked await _userService .IsTokenRevoked(principal.FindFirstValue(JwtRegisteredClaimNames.Jti)); if (tokenRevoked) { context.Response.StatusCode 401; return; } } catch (SecurityTokenException) { context.Response.StatusCode 401; return; } } await _next(context); } }3. Vue前端安全集成方案3.1 路由守卫高级实现增强版路由守卫需处理以下场景从其他系统带token跳转本地token自动续期权限粒度控制// src/router/guards.js export function createSSOGuard(router, store) { router.beforeEach(async (to, from, next) { // 白名单路由 if (isWhiteRoute(to)) return next() // 检查是否携带跨系统token const { token, system } to.query if (token system) { try { // 验证跨系统token const { data } await authService.validateCrossToken(token, system) // 存储会话信息 store.commit(user/SET_TOKEN, data.token) store.commit(user/SET_USER, data.user) store.commit(permission/SET_ROUTES, data.routes) // 清除URL中的token参数 const cleanQuery { ...to.query } delete cleanQuery.token delete cleanQuery.system return next({ path: to.path, query: cleanQuery, replace: true }) } catch (error) { return next(/login?redirect${to.fullPath}) } } // 检查本地会话 if (store.getters[user/token]) { // 令牌自动续期检查 if (isTokenExpiringSoon(store.getters[user/token])) { try { await store.dispatch(user/refreshToken) } catch (error) { return next(/login?redirect${to.fullPath}) } } // 动态权限验证 if (!hasPermission(to, store.getters[user/permissions])) { return next(/403) } return next() } // 跳转登录页保留目标路由 next(/login?redirect${to.fullPath}) }) }3.2 安全跳转组件封装安全的系统跳转组件template a :hreffinalUrl click.preventhandleJump classsso-link slot/slot /a /template script export default { name: SsoLink, props: { system: { type: String, required: true }, path: { type: String, default: / } }, computed: { finalUrl() { return ${this.systemConfig[this.system].baseUrl}${this.path} } }, methods: { async handleJump() { try { // 获取目标系统令牌 const { token } await this.$api.auth.getCrossSystemToken( this.system, this.$store.getters[user/userId] ) // 构建安全跳转URL const url new URL(this.finalUrl) url.searchParams.set(token, token) url.searchParams.set(system, this.$store.state.app.currentSystem) // 记录跳转日志 await this.$api.log.addNavigationLog({ from: window.location.href, to: url.toString(), system: this.system }) // 跳转前清除敏感数据 this.$store.commit(user/CLEAR_SENSITIVE_DATA) window.location.href url.toString() } catch (error) { this.$message.error(系统跳转失败: ${error.message}) } } } } /script4. 性能优化与安全增强4.1 分布式会话管理方案方案类型优点缺点适用场景JWT自包含无状态、扩展性好无法主动失效短期令牌场景Redis共享会话可主动控制依赖基础设施高安全要求场景数据库存储数据持久化性能瓶颈审计严格场景推荐混合方案实现services.AddAuthentication(options { options.DefaultScheme hybrid; options.DefaultChallengeScheme oidc; }) .AddPolicyScheme(hybrid, 混合认证策略, options { options.ForwardDefaultSelector context { // API请求使用JWT if (context.Request.Path.StartsWithSegments(/api)) return JwtBearerDefaults.AuthenticationScheme; // 页面跳转使用Cookie return CookieAuthenticationDefaults.AuthenticationScheme; }; }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options { options.TokenValidationParameters new TokenValidationParameters { ValidateIssuer true, ValidateAudience true, ValidateLifetime true, ClockSkew TimeSpan.FromMinutes(1), // 其他配置... }; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options { options.Cookie.HttpOnly true; options.Cookie.SameSite SameSiteMode.Lax; options.Cookie.SecurePolicy CookieSecurePolicy.Always; options.SlidingExpiration true; options.ExpireTimeSpan TimeSpan.FromHours(8); // 其他配置... });4.2 安全防护措施清单传输层防护强制HTTPSHSTS头设置CSP内容安全策略令牌安全短期有效期常规操作≤8小时使用HMAC-SHA256签名算法关键操作需二次认证审计监控CREATE TABLE sso_audit_log ( id BIGINT PRIMARY KEY, user_id VARCHAR(36) NOT NULL, action VARCHAR(50) NOT NULL, system_from VARCHAR(50) NOT NULL, system_to VARCHAR(50), ip_address VARCHAR(45), user_agent TEXT, created_at DATETIME2 DEFAULT SYSDATETIME(), INDEX ix_user_action (user_id, action), INDEX ix_time (created_at) );5. 企业级部署实践在Kubernetes集群中部署SSO服务的建议配置# sso-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: sso-service spec: replicas: 3 selector: matchLabels: app: sso-service template: metadata: labels: app: sso-service spec: containers: - name: sso image: your-registry/sso-service:1.5.0 ports: - containerPort: 80 envFrom: - configMapRef: name: sso-config - secretRef: name: sso-secrets resources: limits: cpu: 1 memory: 1Gi requests: cpu: 500m memory: 512Mi livenessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 80 initialDelaySeconds: 5 periodSeconds: 5关键配置项多可用区部署确保至少3个Pod分布在不同AZHPA配置根据QPS自动扩缩容网络策略限制只有前端Ingress可以访问/auth端点密钥管理使用Vault或KMS管理签名密钥实际部署中我们发现当并发用户超过5000时Redis会成为性能瓶颈。解决方案是采用Redis Cluster分片存储会话数据同时为高频访问的用户数据添加本地内存缓存层。