
使用ASP.NET Core和IdentityServer4实现单点登录系统:从零构建一个安全的身份认证中心
大家好,作为一名在.NET生态里摸爬滚打多年的开发者,我深知在微服务或分布式系统中,统一身份认证和授权是个绕不开的“硬骨头”。每次新开一个服务,都要重复造一遍登录的轮子,用户体验割裂,维护成本也高。直到我遇见了IdentityServer4(简称IDS4),它基于OpenID Connect和OAuth 2.0协议,堪称ASP.NET Core下实现单点登录(SSO)的“瑞士军刀”。今天,我就带大家手把手搭建一个最小可用的单点登录系统,过程中我也会分享一些实战中踩过的坑和心得。
第一步:项目准备与环境搭建
首先,我们需要创建两个核心项目:一个是作为“安全令牌服务(STS)”的IdentityServer4中心,另一个是作为“客户端”的Web应用。这里我假设你已经安装了.NET 6或更高版本的SDK。
打开终端,执行以下命令:
# 创建解决方案目录并进入
mkdir IdentityServerDemo
cd IdentityServerDemo
# 创建IdentityServer4认证中心项目(使用空模板)
dotnet new web -n IdentityServer
cd IdentityServer
# 添加IdentityServer4包(注意:由于IdentityServer4已停止对新版本.NET的主动支持,我们使用其社区维护的Duende Software版本,但为便于理解,本文仍沿用IdentityServer4概念)
dotnet add package Duende.IdentityServer
cd ..
# 创建客户端Web应用项目(使用MVC模板)
dotnet new mvc -n MvcClient
cd MvcClient
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect
cd ..
踩坑提示:IdentityServer4官方已转向商业化的Duende IdentityServer,对于学习和测试,我们可以使用其免费的社区版。上述命令安装的 `Duende.IdentityServer` 包是兼容的继承者,API与IDS4高度相似。务必注意许可证变化,生产环境请仔细阅读Duende的授权条款。
第二步:配置IdentityServer4认证中心
现在,我们来武装我们的认证中心。编辑 `IdentityServer` 项目中的 `Program.cs` 文件。
首先,定义一些测试用户和客户端配置。在实际项目中,这些信息通常来自数据库。我们在 `Program.cs` 中添加:
using Duende.IdentityServer;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Test;
var builder = WebApplication.CreateBuilder(args);
// 添加IdentityServer服务,并配置内存中的测试资源和客户端
builder.Services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddTestUsers(TestUsers.Users) // 添加测试用户
.AddDeveloperSigningCredential(); // 开发环境签名凭证,生产环境需使用持久化密钥
// ... 其余服务配置
var app = builder.Build();
// 中间件配置
app.UseStaticFiles();
app.UseRouting();
// 启用IdentityServer中间件
app.UseIdentityServer();
app.UseAuthorization();
app.MapGet("/", () => "IdentityServer is running!");
app.Run();
接下来,在项目根目录创建一个 `Config.cs` 文件,用于存放配置数据:
namespace IdentityServer;
public static class Config
{
// OpenID Connect 标准身份资源
public static IEnumerable IdentityResources =>
new[]
{
new IdentityResources.OpenId(), // 必须的openid
new IdentityResources.Profile(), // 用户基本信息(如姓名)
};
// API作用域
public static IEnumerable ApiScopes =>
new[] { new ApiScope("api1", "My API") };
// 客户端定义(这里配置我们的MvcClient)
public static IEnumerable Clients =>
new[]
{
new Client
{
ClientId = "mvc_client",
ClientSecrets = { new Secret("secret".Sha256()) }, // 客户端密码
AllowedGrantTypes = GrantTypes.Code, // 授权码模式,最安全
RedirectUris = { "https://localhost:5002/signin-oidc" }, // 登录后回调地址
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, // 登出后回调地址
AllowedScopes = new List
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
RequirePkce = true, // 启用PKCE增强安全性
AllowOfflineAccess = true // 允许刷新令牌
}
};
}
// 测试用户
public static class TestUsers
{
public static List Users => new()
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "alice",
Claims = new[]
{
new System.Security.Claims.Claim("name", "Alice"),
new System.Security.Claims.Claim("website", "https://alice.com")
}
}
};
}
实战经验:`RedirectUris` 和 `PostLogoutRedirectUris` 必须与客户端应用的地址完全匹配,包括端口,否则会报“无效的重定向URI”错误,这是我初学时最常遇到的坑之一。
第三步:配置客户端(MVC应用)以使用单点登录
现在切换到 `MvcClient` 项目。我们需要让它信任并连接到我们的认证中心。
首先,修改 `MvcClient` 的 `appsettings.json`,添加IdentityServer配置:
{
"Authentication": {
"Authority": "https://localhost:5001", // IdentityServer地址
"ClientId": "mvc_client",
"ClientSecret": "secret"
},
// ... 其他配置
}
然后,编辑 `Program.cs`,添加认证服务:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
// 添加认证服务
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; // Cookie作为默认方案处理本地登录
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; // 当需要认证时,跳转到IDS4
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) // 添加Cookie处理程序
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => // 添加OpenID Connect处理程序
{
options.Authority = builder.Configuration["Authentication:Authority"];
options.ClientId = builder.Configuration["Authentication:ClientId"];
options.ClientSecret = builder.Configuration["Authentication:ClientSecret"];
options.ResponseType = "code"; // 使用授权码模式
options.SaveTokens = true; // 在Cookie中保存从IDS4返回的令牌
// 配置作用域
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("api1");
// 获取用户信息的声明
options.GetClaimsFromUserInfoEndpoint = true;
});
// 添加授权服务
builder.Services.AddAuthorization();
// 将MVC服务改为使用控制器和视图
builder.Services.AddControllersWithViews();
var app = builder.Build();
// ... 配置HTTP请求管道
app.UseStaticFiles();
app.UseRouting();
// **重要顺序**:先认证,再授权
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
最后,我们修改一下 `HomeController` 的 `Index` 视图,显示登录状态和用户声明。在 `Views/Home/Index.cshtml` 中简单添加:
@using Microsoft.AspNetCore.Authentication
@{
ViewData["Title"] = "Home Page";
}
Claims
@foreach (var claim in User.Claims)
{
- @claim.Type
- @claim.Value
}
Tokens
@if (await Context.GetTokenAsync("access_token") != null)
{
- access_token
- @(await Context.GetTokenAsync("access_token"))
}
第四步:运行与测试
激动人心的时刻到了!我们需要同时运行两个项目。你可以使用多个终端窗口,或者配置IDE的多启动项目。这里我用命令行演示:
# 第一个终端,启动IdentityServer (在IdentityServer目录下)
dotnet run --urls="https://localhost:5001"
# 第二个终端,启动MvcClient (在MvcClient目录下)
dotnet run --urls="https://localhost:5002"
打开浏览器,访问 https://localhost:5002。此时,应用会检测到你未登录,自动跳转到IdentityServer的登录页面(https://localhost:5001)。
使用我们配置的测试用户:用户名 alice,密码 alice 登录。登录成功后,你会被重定向回MVC客户端,并且页面上会显示你的用户信息(Claims)和访问令牌(Access Token)。这标志着单点登录的核心流程已经打通!
踩坑提示:如果遇到HTTPS证书错误(尤其在Windows上),可以在项目属性中信任开发证书,或者临时在 `Program.cs` 的 `builder` 构建后添加 `builder.WebHost.UseSetting(“https_port”, “5001”)` 并暂时使用HTTP(仅限开发环境)。但为了符合OAuth 2.0安全最佳实践,强烈建议始终使用HTTPS。
总结与展望
至此,我们已经成功搭建了一个最基本的单点登录系统。你可能会觉得,这离“生产可用”还有距离。没错,我们还需要考虑:
- 持久化存储:将客户端、资源、用户数据从内存移到数据库(如使用 `AddConfigurationStore` 和 `AddOperationalStore`)。
- 正式密钥管理:替换 `AddDeveloperSigningCredential`,使用X.509证书或RSA密钥。
- 用户管理界面:集成ASP.NET Core Identity,提供用户注册、密码找回等功能。
- 更多客户端类型:配置SPA(如Vue、React)、移动App或后端服务间的通信。
IdentityServer4(及其后继者Duende IdentityServer)的功能非常强大,本文只是揭开了冰山一角。它妥善处理了令牌生命周期、刷新、撤销以及复杂的联合登录场景。希望这篇教程能为你打开单点登录和身份认证领域的大门,让你在构建现代分布式应用时,多一份从容和自信。记住,安全无小事,每一步配置都值得仔细推敲。Happy coding!


评论(0)