C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#实现SSO单点登录

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

作者:阿登林

单点登录即SSO(Single Sign On),是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,这篇文章主要介绍了C#中实现SSO单点登录的常用方案及实际案例,需要的朋友可以参考下

一、为什么我们需要SSO?

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

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

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

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

核心流程

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

三、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)自定义开发。

核心代码实现

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解决方案,如:

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

四、SSO实现中的关键考量

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

4.1 安全性

4.2 跨域问题

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

4.3 会话管理

4.4 高可用性

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

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

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

场景描述

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

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

实现架构

核心配置

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

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单点登录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文