C#中实现SSO单点登录的常用方案及实际案例

 更新时间:2025年09月09日 09:43:22   作者:阿登林  
单点登录即SSO(Single Sign On),是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,这篇文章主要介绍了C#中实现SSO单点登录的常用方案及实际案例,需要的朋友可以参考下

一、为什么我们需要SSO?

想象这样一个场景:你每天上班需要登录公司的OA系统审批文件,登录CRM系统查看客户信息,登录财务系统报销费用,登录人力资源系统提交休假申请...每个系统都有不同的账号密码,每天光是输入密码都要花费不少时间,更不用说时不时还要应对密码过期、忘记密码的困扰。

这就是单点登录(Single Sign-On,简称SSO)技术要解决的核心问题。SSO让用户只需登录一次,就能访问多个相互信任的应用系统,极大提升了用户体验和工作效率。在企业级应用架构中,SSO已经成为不可或缺的基础设施。

二、SSO单点登录的基本原理

SSO的实现基于一个关键概念:认证中心。所有应用系统不再各自为政地管理用户认证,而是将认证请求委托给统一的认证中心处理。

核心流程

  • 用户访问应用系统A,发现未登录

  • 应用系统A将用户重定向到认证中心

  • 用户在认证中心输入凭证进行登录

  • 认证中心验证通过后,生成一个全局会话,并颁发一个令牌(Token)

  • 用户带着令牌重定向回应用系统A

  • 应用系统A验证令牌的有效性,确认后建立本地会话

  • 当用户访问应用系统B时,同样重定向到认证中心

  • 认证中心发现用户已有全局会话,直接颁发令牌并重定向回应用系统B

整个过程中,用户只需输入一次账号密码,就能无缝访问多个系统。

三、C#中实现SSO的常用方案

在C#生态系统中,我们有多种实现SSO的方案,下面介绍几种主流的实现方式。

3.1 基于ASP.NET Identity + OAuth 2.0

ASP.NET Identity是微软官方提供的身份验证框架,结合OAuth 2.0协议可以轻松实现SSO。

实现步骤

  • 创建认证服务器

首先,我们需要创建一个专门的认证服务器项目:

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // 配置身份验证
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    
    services.AddDefaultIdentity<IdentityUser>(options =>
        options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
    // 配置认证服务器
    services.AddIdentityServer()
        .AddApiAuthorization<IdentityUser, ApplicationDbContext>();
    
    services.AddAuthentication()
        .AddIdentityServerJwt();
}
​
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    app.UseAuthentication();
    app.UseIdentityServer();
    app.UseAuthorization();
    // ...
}
  • 配置客户端应用

在需要接入SSO的客户端应用中,添加如下配置:

// appsettings.json
"IdentityServer": {
  "Authority": "https://your-auth-server.com",
  "ClientId": "your-client-id",
  "ClientSecret": "your-client-secret",
  "ResponseType": "code"
}
​
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {
        options.Authority = "https://your-auth-server.com";
        options.ClientId = "your-client-id";
        options.ClientSecret = "your-client-secret";
        options.ResponseType = "code";
        options.Scope.Add("profile");
        options.Scope.Add("email");
        options.SaveTokens = true;
    });
}

3.2 基于JWT令牌的自定义实现

如果需要更灵活的SSO实现,可以基于JWT(JSON Web Token)自定义开发。

核心代码实现

  • JWT工具类

public class JwtHelper
{
    private readonly string _secretKey;
    private readonly string _issuer;
    private readonly string _audience;
    
    public JwtHelper(IConfiguration configuration)
    {
        _secretKey = configuration["Jwt:SecretKey"];
        _issuer = configuration["Jwt:Issuer"];
        _audience = configuration["Jwt:Audience"];
    }
    
    public string GenerateToken(string userId, string username, IEnumerable<string> roles)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, userId),
            new Claim(ClaimTypes.Name, username)
        };
        
        foreach (var role in roles)
        {
            claims.Add(new Claim(ClaimTypes.Role, role));
        }
        
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secretKey));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        
        var token = new JwtSecurityToken(
            issuer: _issuer,
            audience: _audience,
            claims: claims,
            expires: DateTime.Now.AddHours(1),
            signingCredentials: credentials
        );
        
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
    
    public bool ValidateToken(string token, out ClaimsPrincipal principal)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_secretKey);
        
        try
        {
            principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidIssuer = _issuer,
                ValidAudience = _audience,
                ClockSkew = TimeSpan.Zero
            }, out _);
            
            return true;
        }
        catch
        {
            principal = null;
            return false;
        }
    }
}
  • 认证中心控制器

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly JwtHelper _jwtHelper;
    private readonly IUserService _userService;
    
    public AuthController(JwtHelper jwtHelper, IUserService userService)
    {
        _jwtHelper = jwtHelper;
        _userService = userService;
    }
    
    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        var user = await _userService.ValidateCredentials(request.Username, request.Password);
        
        if (user == null)
        {
            return Unauthorized("用户名或密码错误");
        }
        
        var roles = await _userService.GetUserRoles(user.Id);
        var token = _jwtHelper.GenerateToken(user.Id.ToString(), user.UserName, roles);
        
        // 设置SSO Cookie
        Response.Cookies.Append("SSO_TOKEN", token, new CookieOptions
        {
            HttpOnly = true,
            Secure = true,
            SameSite = SameSiteMode.None,
            Domain = ".yourcompany.com", // 注意设置为父域名
            Expires = DateTime.Now.AddHours(1)
        });
        
        return Ok(new { Token = token, UserId = user.Id, Username = user.UserName });
    }
    
    [HttpGet("check")]
    public IActionResult CheckLogin()
    {
        if (Request.Cookies.TryGetValue("SSO_TOKEN", out var token))
        {
            if (_jwtHelper.ValidateToken(token, out var principal))
            {
                return Ok(new { IsLoggedIn = true, UserInfo = principal.Identity.Name });
            }
        }
        
        return Ok(new { IsLoggedIn = false });
    }
    
    [HttpPost("logout")]
    public IActionResult Logout()
    {
        Response.Cookies.Delete("SSO_TOKEN", new CookieOptions
        {
            Domain = ".yourcompany.com",
            SameSite = SameSiteMode.None,
            Secure = true
        });
        
        return Ok("退出成功");
    }
}

3.3 使用第三方SSO解决方案

对于企业级应用,我们也可以考虑使用成熟的第三方SSO解决方案,如:

  • Microsoft Azure AD:提供完整的身份和访问管理解决方案

  • Okta:功能强大的身份管理平台

  • Auth0:易于集成的认证服务

这些解决方案通常提供丰富的API和SDK,大大简化了SSO的实现难度。

四、SSO实现中的关键考量

在实现SSO时,有几个关键因素需要特别注意:

4.1 安全性

  • 使用HTTPS:确保所有认证请求都通过加密通道传输

  • 令牌保护:使用HttpOnly和Secure标志保护SSO令牌

  • 令牌有效期:设置合理的令牌有效期,平衡安全性和用户体验

  • 签名验证:确保令牌的完整性和真实性

4.2 跨域问题

在Web应用中实现SSO,跨域问题是绕不开的挑战。解决方法包括:

  • 设置共享的父域名Cookie

  • 使用CORS(跨域资源共享)策略

  • 通过iframe或postMessage进行跨域通信

4.3 会话管理

  • 全局会话:认证中心需要维护全局会话状态

  • 单点登出:实现一处登出,处处登出的功能

  • 会话超时:合理设置会话超时时间和自动续期机制

4.4 高可用性

认证中心作为所有系统的单点,其高可用性至关重要。可以通过:

  • 部署多个认证中心实例

  • 使用负载均衡

  • 实现故障转移机制

五、实际案例:企业应用SSO集成

让我们看一个实际的企业应用场景,如何将多个不同的应用系统集成到SSO中。

场景描述

某企业有三个主要应用系统:

  • 内部OA系统(ASP.NET MVC)

  • CRM客户管理系统(ASP.NET Core Web API)

  • 人力资源管理系统(Blazor WebAssembly)

现在需要为这三个系统实现SSO,让用户只需登录一次就能访问所有系统。

实现架构

  • 认证中心:基于ASP.NET Core + IdentityServer4构建

  • OA系统:集成OpenID Connect客户端

  • CRM系统:使用JWT Bearer认证

  • HR系统:集成OIDC客户端

核心配置

在认证中心配置三个客户端:

public static IEnumerable<Client> Clients =>
    new Client[]
    {
        new Client
        {
            ClientId = "oa-client",
            ClientName = "内部OA系统",
            AllowedGrantTypes = GrantTypes.Code,
            ClientSecrets = { new Secret("oa-secret".Sha256()) },
            RedirectUris = { "https://oa.yourcompany.com/signin-oidc" },
            PostLogoutRedirectUris = { "https://oa.yourcompany.com/signout-callback-oidc" },
            AllowedScopes = { "openid", "profile", "email" }
        },
        new Client
        {
            ClientId = "crm-client",
            ClientName = "CRM系统",
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            ClientSecrets = { new Secret("crm-secret".Sha256()) },
            AllowedScopes = { "crm-api" }
        },
        new Client
        {
            ClientId = "hr-client",
            ClientName = "人力资源系统",
            AllowedGrantTypes = GrantTypes.Code,
            ClientSecrets = { new Secret("hr-secret".Sha256()) },
            RedirectUris = { "https://hr.yourcompany.com/authentication/login-callback" },
            PostLogoutRedirectUris = { "https://hr.yourcompany.com/authentication/logout-callback" },
            AllowedScopes = { "openid", "profile", "email", "hr-api" }
        }
    };

六、总结与展望

SSO单点登录技术极大地改善了用户在多系统环境下的使用体验,同时也简化了系统管理。在C#开发中,我们可以通过多种方式实现SSO,从自定义开发到使用成熟的第三方解决方案,选择最适合自己项目需求的方式。

随着微服务架构的普及和身份管理技术的发展,SSO也在不断演进。未来,我们可以期待看到更多基于云原生、支持多平台、更加安全便捷的SSO解决方案出现。

对于C#开发者来说,掌握SSO的实现原理和技术方案,不仅能够提升项目的用户体验,也是构建现代化企业应用的必备技能

到此这篇关于C#中实现SSO单点登录的常用方案及实际案例的文章就介绍到这了,更多相关C#实现SSO单点登录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#实现文件筛选读取并翻译的自动化工具

    C#实现文件筛选读取并翻译的自动化工具

    这篇文章主要为大家详细介绍了如何利用C#实现文件筛选及读取内容,并翻译的自动化工具,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2023-03-03
  • C# 崩溃异常中研究页堆布局的详细过程

    C# 崩溃异常中研究页堆布局的详细过程

    最近遇到一位朋友的程序崩溃,发现崩溃点在富编辑器 msftedit 上,这个不是重点,重点在于发现他已经开启了 页堆,由于 页堆 和 NT堆 的内存布局完全不一样,这一篇结合我的了解以及 windbg 验证来系统的介绍下 页堆,需要的朋友可以参考下
    2022-10-10
  • C#数字图象处理之图像灰度化方法

    C#数字图象处理之图像灰度化方法

    这篇文章主要介绍了C#数字图象处理之图像灰度化方法,涉及C#基于Bitmap类操作图像的相关技巧,需要的朋友可以参考下
    2015-04-04
  • C#适用于like语句的SQL格式化函数

    C#适用于like语句的SQL格式化函数

    这篇文章主要介绍了C#适用于like语句的SQL格式化函数,可实现对字符串进行sql格式化,并且符合like查询的格式,非常实用,需要的朋友可以参考下
    2014-10-10
  • C#多线程开发之任务并行库详解

    C#多线程开发之任务并行库详解

    最近在学习C#的并行编程,在每本书上的看到的知识点都不全面,所以先参考多本书书籍的讲解,将并行编程,多线程编程的知识点整理一下,这篇文章主要给大家介绍了关于C#多线程开发之任务并行库的相关资料,需要的朋友可以参考下
    2021-09-09
  • C# Dictionary的使用实例代码

    C# Dictionary的使用实例代码

    C# Dictionary的使用实例代码,需要的朋友可以参考一下
    2013-04-04
  • C#简单的加密类实例

    C#简单的加密类实例

    AesManaged - 高级加密标准(AES) 对称算法的管理类
    2013-03-03
  • C#调用WebService的实现方法

    C#调用WebService的实现方法

    这篇文章主要介绍了C#调用WebService的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • C#使用NPOI对word进行读写

    C#使用NPOI对word进行读写

    这篇文章介绍了C#使用NPOI对word进行读写的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C#学习基础概念二十五问 11-15

    C#学习基础概念二十五问 11-15

    C#学习基础概念二十五问 11-15...
    2007-04-04

最新评论