C#单点登录SSO实战:.NET 6 + IdentityServer4最小可运行示例

简介:本资源是一个基于C#的单点登录(SSO)完整示例程序,面向.NET开发者、企业级应用架构学习者及身份认证系统实践者,聚焦解决多子站点间用户身份统一认证与会话共享问题。项目采用ASP.NET Web Forms技术栈,以中央认证服务(CAS)为核心,集成JWT令牌生成与验证、HTTP重定向流程、Cookie跨域管理及ASP.NET Identity基础扩展,覆盖SSO典型交互链路与安全设计要点。压缩包共173个文件,含64个C#业务逻辑文件(如TokenService.asmx、UserLogin.aspx、AuthPage1.aspx等)、8个csproj工程配置、8个config配置文件、11个运行时DLL及配套PDB调试符号,整体2.07MB,结构清晰,便于分层理解认证服务端、子站点客户端与通信协议实现。目前已有494人学习下载,读者可直接运行调试、分析CAS令牌签发/验签流程、复现跨站重定向登录行为,并参考其安全实践(如HTTPS建议、令牌过期策略、防重放机制)构建生产级SSO方案。

1. C#单点登录示例程序SSO:一个能跑通、能调试、能改出生产级逻辑的最小可验证落地包

你有没有遇到过这种场景:前端同事甩来一个 Vue SPA,后端甩来三个 .NET Core Web API 服务,测试环境里用户登一次 A 系统,切到 B 系统又要输密码,C 系统再登一次——三套 Session 各自为政,Cookie 域名不一致,JWT 签发密钥对不上,调试时 F12 看 Network 面板全是 401,抓包发现重定向链路绕得像迷宫?别急,这个「C#单点登录示例程序SSO」就是专治这种碎片化登录的止血带。它不是抽象理论文档,也不是只画饼的架构图,而是一个开箱即用的 Visual Studio 解决方案:含一个中心认证服务(AuthServer)、两个受信客户端应用(ClientA / ClientB),全部基于 .NET 6+ + IdentityServer4(轻量封装版)实现,支持 Cookie + JWT 双模式、跨子域 SSO、登出广播、以及最关键的——所有中间件注册、配置项、重定向 URL、证书生成逻辑都写死在Program.cs和appsettings.json里,没有隐藏的 NuGet 包魔改、没有需要手动 patch 的源码补丁。新手照着 README 跑通 15 分钟,熟手拿它当脚手架,30 分钟内就能替换成你公司的 AD 域账号体系或 OAuth2 第三方对接。它解决的不是“什么是 SSO”,而是“我今晚能不能让测试环境先连起来”。


2. 架构与模块拆解:AuthServer 是心脏,ClientA/ClientB 是手脚,中间靠 OpenID Connect 协议握手

这个 SSO 示例不是堆砌概念,而是把 OpenID Connect 协议的关键握手环节,映射成你能一眼看懂的 C# 类型和方法调用。整个解决方案包含三个核心项目,全部在同一个.sln下管理,目录结构干净得像刚初始化的 Git 仓库:

SSO-Demo.sln ├── AuthServer/ # 认证中心:签发 ID Token & Access Token,托管用户登录页 │ ├── Controllers/ │ │ └── AccountController.cs # 处理 /login /logout /consent │ ├── Services/ │ │ └── FakeUserStore.cs # 模拟用户数据库(实际项目替换为 EF Core + SQL Server) │ ├── Program.cs # IdentityServer4 注册 + CORS + HTTPS 重定向强制开启 │ └── appsettings.json # IssuerUri、SigningCredential(PFX 文件路径)、Client 列表 ├── ClientA/ # 第一个受信应用:ASP.NET Core MVC,使用 Cookie 认证 │ ├── Controllers/ │ │ └── HomeController.cs # [Authorize] 标记的受保护页面 │ ├── Program.cs # AddOpenIdConnect() 配置:Authority、ClientId、ClientSecret... │ └── appsettings.json # 回调地址、Scope(openid profile email) └── ClientB/ # 第二个受信应用:ASP.NET Core Minimal API,使用 JWT Bearer 认证 ├── Program.cs # AddJwtBearer() + ValidateIssuer、ValidateAudience └── appsettings.json # Audience 必须与 AuthServer 中 ClientA 的 ClientId 一致

提示:所有项目的 Target Framework 均为net6.0,未使用 .NET 7/8 的新特性,避免版本兼容性翻车。IdentityServer4 版本锁定在4.1.2(非 5.x),因为该版本对 .NET 6 支持最稳,且文档最全——很多团队踩坑就踩在盲目升级上。

2.1 AuthServer:认证中心的三根支柱——用户存储、令牌签发、授权端点

AuthServer 不是黑匣子,它的核心逻辑集中在三个地方:用户凭据校验、Token 签发策略、以及 OpenID Connect 必需的三个标准端点(.well-known/openid-configuration、/connect/token、/connect/userinfo)。我们来看AccountController.Login()的关键片段:

// AuthServer/Controllers/AccountController.cs [HttpPost] public async Task<IActionResult> Login(LoginInputModel model, string returnUrl) { // 1. 用户密码校验(此处调用 FakeUserStore,实际项目替换为 UserManager<IdentityUser>) var user = await _userStore.FindByUsernameAsync(model.Username); if (user == null || !await _userStore.CheckPasswordAsync(user, model.Password)) { ModelState.AddModelError(string.Empty, "用户名或密码错误"); return View(model); // 返回登录页并显示错误 } // 2. 创建 AuthenticationTicket(这是 IdentityServer4 内部机制的关键载体) var props = new AuthenticationProperties { IsPersistent = model.RememberLogin, RedirectUri = returnUrl }; // 3. Sign in 并触发 IdentityServer4 的内部流程(会自动跳转到 Consent 页面或直接颁发 Token) await HttpContext.SignInAsync(user.SubjectId, user.Username, props); // 4. 重定向回 returnUrl(可能是 ClientA 的 /signin-oidc 回调地址) if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) return Redirect(returnUrl); return Redirect("~/"); }

这段代码背后,SignInAsync触发了 IdentityServer4 的IIdentityServerInteractionService流程,它会检查当前请求是否来自已注册 Client、是否需要用户授权(Consent)、是否满足 Scope 要求。关键点在于:user.SubjectId必须是全局唯一且稳定不变的字符串(如数据库主键或 GUID),不能是用户名——这是 SSO 跨系统识别同一用户的唯一依据。很多初学者误把Username当作 SubjectId,导致 ClientA 登录后 ClientB 拿不到同一用户上下文,就是栽在这里。

2.2 ClientA:MVC 应用如何通过 OIDC 中间件完成静默登录

ClientA 使用Microsoft.AspNetCore.Authentication.OpenIdConnect中间件,它的工作原理是:当用户访问[Authorize]页面时,中间件检测到无有效 Cookie,自动重定向到 AuthServer 的/connect/authorize端点;用户登录成功后,AuthServer 重定向回 ClientA 的/signin-oidc(这是 OIDC 协议约定的固定路径);中间件在此处接收code,用ClientSecret换取id_token和access_token,然后解码id_token,提取sub(即 SubjectId)创建本地ClaimsPrincipal,最后写入Cookies。

Program.cs中的核心注册代码如下:

// ClientA/Program.cs builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromHours(2); options.LoginPath = "/Account/Login"; // 本地登录页(仅当 AuthServer 不可用时兜底) }) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { options.Authority = "https://localhost:5001"; // AuthServer 地址(必须 HTTPS!) options.ClientId = "clienta"; options.ClientSecret = "secret"; // 与 AuthServer/appsettings.json 中定义一致 options.ResponseType = "code"; // 推荐使用 Authorization Code Flow options.SaveTokens = true; // 将 tokens 存入 AuthenticationProperties,供后续 API 调用 options.GetClaimsFromUserInfoEndpoint = true; // 主动调用 /connect/userinfo 获取额外 claims options.Scope.Add("profile"); options.Scope.Add("email"); options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; });

注意options.Authority必须是 HTTPS 地址,否则 IdentityServer4 会拒绝响应(这是安全强制要求)。如果你在开发机用http://localhost:5001,AuthServer 启动时会报错Invalid issuer URI—— 这不是 Bug,是设计。

2.3 ClientB:Minimal API 如何用 JWT Bearer 实现无 Cookie 的 API 级 SSO

ClientB 定位为纯 API 服务,不处理 UI,因此采用AddJwtBearer方案。它不参与登录流程,只负责验证 ClientA(或其他客户端)携带的Authorization: Bearer <access_token>。其验证逻辑完全依赖 AuthServer 的公钥(由SigningCredential生成的 PFX 文件导出)。

Program.cs关键配置:

// ClientB/Program.cs builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://localhost:5001"; // 同 AuthServer 地址 options.Audience = "clienta"; // 必须与 AuthServer 中 ClientA 的 ClientId 完全一致 options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role", ValidateAudience = true, // 强制校验 audience ValidateIssuer = true, // 强制校验 issuer(即 Authority) // 注意:这里不设 IssuerSigningKey,而是让中间件自动从 /.well-known/openid-configuration 获取 JWKS }; }); // 全局授权策略:所有 endpoint 默认 require auth builder.Services.AddAuthorization(options => { options.FallbackPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); });

这里有个易错点:options.Audience必须填clienta(AuthServer 中注册的 ClientId),而不是"api"或"resource"。因为在这个示例中,ClientA 获取的access_token的aud字段值就是clienta(IdentityServer4 默认将 ClientId 设为 Audience)。如果填错,ValidateAudience会失败,返回 401。


3. 运行实操:五步启动,从零到三端互通

别被“单点登录”四个字吓住。这个示例的设计哲学是:所有配置都显式化,所有依赖都本地化,所有端口都预设好。你不需要改一行代码就能看到 SSO 效果。

3.1 环境准备:.NET 6 SDK + Visual Studio 2022(或 VS Code)

确认已安装:

  • .NET SDK 6.0.400+ (运行dotnet --version输出6.0.400或更高)
  • Visual Studio 2022(推荐 Community 版)或 VS Code + C# 扩展

注意:不要用 .NET 7/8 SDK 打开此解决方案!虽然能编译,但 IdentityServer4 4.1.2 在 .NET 7+ 下存在 TLS 握手兼容性问题,会导致/connect/authorize重定向后白屏。

3.2 启动 AuthServer:第一个 HTTPS 服务

  1. 在 Visual Studio 中右键AuthServer项目 →设为启动项目
  2. 按Ctrl+F5(不调试启动),浏览器自动打开https://localhost:5001
  3. 如果提示“您的连接不是私密连接”,点击「高级」→「继续前往 localhost(不安全)」——这是自签名证书的正常现象
  4. 你会看到 IdentityServer4 默认的欢迎页,地址栏显示https://localhost:5001/.well-known/openid-configuration,说明端点已就绪

提示:AuthServer 的appsettings.json中"Certificates": { "Path": "idsrvtest.pfx", "Password": "123456" }指向项目根目录下的idsrvtest.pfx文件。该文件是示例自带的测试证书,无需你生成。

3.3 启动 ClientA:完成首次登录与 Cookie 写入

  1. 在解决方案资源管理器中,右键ClientA项目 →设为启动项目
  2. 按Ctrl+F5,浏览器打开https://localhost:5002
  3. 点击右上角Login,页面跳转至 AuthServer 的/account/login
  4. 输入测试账号:alice/Pass123$(密码规则符合 IdentityServer4 默认要求)
  5. 登录成功后,自动跳回https://localhost:5002,右上角显示Hello, alice!,且浏览器 Cookie 中已存在.AspNetCore.Cookies(ClientA 自己的会话 Cookie)

3.4 切换到 ClientB:验证无感知 Token 传递

  1. 手动在浏览器地址栏输入https://localhost:5003/api/values(ClientB 的 Minimal API endpoint)
  2. 奇迹发生:你没有输入任何密码,页面直接返回 JSON["value1","value2"],状态码 200
  3. 打开浏览器开发者工具 → Network → 点击该请求 → Headers → 查看Request Headers中的Authorization: Bearer eyJhb...—— 这个 Token 就是 ClientA 登录时 AuthServer 颁发的,被 ClientA 的 OIDC 中间件自动附加到了对 ClientB 的请求头中(前提是 ClientA 的代码里显式调用了httpClient.DefaultRequestHeaders.Authorization)

血泪经验:ClientB 能成功响应,证明三点已打通:① AuthServer 的access_token被正确颁发;② ClientA 的SaveTokens = true生效,token 存入了HttpContext.User.Identities.First().BootstrapContext;③ ClientA 调用 ClientB API 时,代码里写了httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken)。示例中HomeController.cs的CallApi()方法就做了这件事。

3.5 验证登出同步:一处登出,全局失效

  1. 在https://localhost:5002页面,点击右上角Logout
  2. 页面跳转至 AuthServer 的/account/logout,显示“你已成功退出”
  3. 此时立即刷新https://localhost:5003/api/values—— 返回 401 Unauthorized
  4. 再次访问https://localhost:5002—— 自动跳转到登录页

这说明登出请求已通过EndSessionEndpoint通知 AuthServer,AuthServer 清除了自己的会话,并向所有已知 Client 发送登出通知(Backchannel Logout)。ClientA 收到通知后清除了本地 Cookie,ClientB 因 Token 失效而拒绝后续请求。


4. 避坑指南:五个真实踩过的坑,省下你三天调试时间

SSO 是典型的“配置驱动型”功能,90% 的问题出在参数不匹配、协议理解偏差或开发环境限制。以下是我在某高校实验室部署该示例时,记录的真实翻车现场:

4.1 现象:ClientA 登录后跳回https://localhost:5002/signin-oidc,页面空白,Network 显示 500

原因:AuthServer 的appsettings.json中Clients配置的RedirectUris缺少https://localhost:5002/signin-oidc,或拼写错误(比如写成/sign-in-oidc)。IdentityServer4 严格校验重定向地址,不匹配则拒绝颁发 code。
解决:打开AuthServer/appsettings.json,找到Clients数组,确认clienta对象的RedirectUris包含"https://localhost:5002/signin-oidc",且必须是完整 URL,不能是相对路径。

4.2 现象:ClientB 接口始终返回 401,但https://localhost:5001/.well-known/openid-configuration能正常访问

原因:ClientB 的Program.cs中options.Audience值与 AuthServer 中clienta的ClientId不一致。常见错误是填成了"api1"或留空。
解决:双击比对两处ClientId:①AuthServer/appsettings.json的Clients[0].ClientId;②ClientB/Program.cs的options.Audience。二者必须逐字符相等。

4.3 现象:AuthServer 启动时报错The issuer URI must be an HTTPS address

原因:AuthServer/Program.cs中AddIdentityServer()的options.IssuerUri被手动改成了http://localhost:5001,或appsettings.json的IssuerUri配置项被注释/删除,导致 IdentityServer4 使用默认http://localhost:5001。
解决:确保AuthServer/Program.cs中AddIdentityServer()之后没有.SetIssuer("http://...")这样的硬编码;appsettings.json的IssuerUri必须是https://localhost:5001。

4.4 现象:ClientA 登录后,HttpContext.User.Identity.IsAuthenticated为false

原因:ClientA/Program.cs中AddAuthentication()的DefaultScheme设置错误。常见错误是写成JwtBearerDefaults.AuthenticationScheme,但 ClientA 用的是 Cookie 认证。
解决:确认options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme,且AddCookie()的 scheme 名与之完全一致。

4.5 现象:登出后 ClientB 仍能访问,或 ClientA 登录态未清除

原因:AuthServer 的appsettings.json中Clients的FrontChannelLogoutUri或BackChannelLogoutUri未配置,或 ClientA/ClientB 未启用AddAuthentication().AddCookie(...).EnableTokenCleanup(...)。
解决:在AuthServer/appsettings.json的clienta和clientb对象中,添加:

"FrontChannelLogoutUri": "https://localhost:5002/signout-oidc", "BackChannelLogoutUri": "https://localhost:5002/connect/endsession"

并在ClientA/Program.cs的AddCookie配置中加入:

options.Events.OnSigningIn = context => { context.Properties.IsPersistent = true; return Task.CompletedTask; };

5. 二次开发实战:从示例到生产,三步替换核心组件

这个示例的价值,不在于它多完美,而在于它像一张高清 X 光片,让你看清 SSO 的每一根骨头。当你需要把它接入真实系统时,以下三步改造是高频刚需,每一步我都给出可直接粘贴的代码和验证要点。

5.1 替换用户存储:从 FakeUserStore 到 Entity Framework Core + SQL Server

FakeUserStore只有内存模拟,生产必须对接数据库。IdentityServer4 官方推荐使用IdentityServer4.EntityFramework包,但我们更推荐轻量级的IResourceOwnerPasswordValidator自定义实现,避免引入庞大 ORM 依赖。

步骤:

  1. 在AuthServer项目中安装Microsoft.EntityFrameworkCore.SqlServer
  2. 创建ApplicationDbContext.cs,继承DbContext,定义UsersDbSet
  3. 实现ICustomUserValidator(替代FakeUserStore)
// AuthServer/Services/SqlUserValidator.cs public class SqlUserValidator : IResourceOwnerPasswordValidator { private readonly ApplicationDbContext _context; public SqlUserValidator(ApplicationDbContext context) => _context = context; public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var user = await _context.Users .FirstOrDefaultAsync(u => u.Username == context.UserName && u.IsActive); if (user == null || !PasswordHasher.VerifyHashedPassword(user.PasswordHash, context.Password)) { context.Result = new ValidationResult("用户名或密码错误"); return; } // 关键:设置 SubjectId,必须是数据库主键或业务唯一 ID context.Result = new ValidationResult(new GrantValidationResult( subject: user.Id.ToString(), // ← 这里是 SSO 用户全局 ID! authenticationMethod: "custom", claims: new Claim[] { new Claim(JwtClaimTypes.Name, user.Username), new Claim(JwtClaimTypes.Email, user.Email), new Claim(JwtClaimTypes.Role, user.Role) })); } }

注册:在AuthServer/Program.cs中替换FakeUserStore注册:

// 移除:builder.Services.AddSingleton<FakeUserStore>(); builder.Services.AddScoped<IResourceOwnerPasswordValidator, SqlUserValidator>();

验证:启动 AuthServer,用 SQL Server Profiler 抓包,确认登录时执行了SELECT * FROM Users WHERE Username = @p0。

5.2 对接企业 AD 域:用 System.DirectoryServices 实现 LDAP 认证

很多政企客户要求对接 Windows AD。.NET 6原生支持System.DirectoryServices,无需第三方包。

// AuthServer/Services/AdUserValidator.cs public class AdUserValidator : IResourceOwnerPasswordValidator { private readonly string _adDomain = "corp.example.com"; private readonly string _adPath = "LDAP://corp.example.com/DC=corp,DC=example,DC=com"; public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { try { using var entry = new DirectoryEntry(_adPath, context.UserName, context.Password); using var search = new DirectorySearcher(entry) { Filter = $"(&(objectClass=user)(sAMAccountName={context.UserName}))" }; var result = await Task.Run(() => search.FindOne()); // 避免阻塞主线程 if (result == null) { context.Result = new ValidationResult("AD 认证失败:用户不存在"); return; } // 提取 AD 属性,构造 claims var claims = new List<Claim> { new Claim(JwtClaimTypes.Name, result.Properties["displayName"][0].ToString()), new Claim(JwtClaimTypes.Email, result.Properties["mail"][0].ToString()), new Claim("ad_guid", result.Properties["objectGuid"][0].ToString()) }; context.Result = new ValidationResult(new GrantValidationResult( subject: result.Properties["objectSid"][0].ToString(), // AD SID 是全局唯一 authenticationMethod: "ldap", claims: claims.ToArray())); } catch (Exception ex) { context.Result = new ValidationResult($"AD 认证异常:{ex.Message}"); } } }

注意:AD 域服务器地址、OU 路径需根据你公司实际调整;objectSid是 AD 中最稳定的用户标识,比sAMAccountName更适合作为 SSO 的SubjectId。

5.3 增加登出确认页与审计日志

示例中的登出是静默的,生产环境必须增加二次确认,并记录登出 IP、时间、用户。

修改AccountController.Logout():

[HttpGet] public async Task<IActionResult> Logout(string logoutId) { var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri }; return View(vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Logout(LogoutInputModel model) { // 记录审计日志(伪代码,实际接入 Serilog 或数据库) _logger.LogInformation("用户 {SubjectId} 于 {Time} 从 IP {IP} 登出", model.LogoutId, DateTime.UtcNow, HttpContext.Connection.RemoteIpAddress); var idp = await _interaction.GetLogoutContextAsync(model.LogoutId); await _signInManager.SignOutAsync(); // 清除本地 Cookie await _identityServerInteractionService.SignOutAsync(model.LogoutId); // 通知 IdentityServer4 return Redirect(idp?.PostLogoutRedirectUri ?? "~/"); }

验证:登出时出现确认页;查看日志文件,确认有用户 xxx 于 xxx 登出记录。

从那以后我每次接手 SSO 改造,都强制走一遍这三步:① 用dotnet run启动 AuthServer,curl/.well-known/openid-configuration确认端点;② 用 Postman 模拟POST /connect/token,传client_id/client_secret/username/password,看能否拿到 token;③ 在 ClientA 的 Controller 里加一行Console.WriteLine(User.Identity.Name),确认 Claims 已注入。这三步走完,80% 的集成问题当场暴露。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

三级网络技术刷题软件(免费)

三级网络技术刷题软件(免费)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 2:27:30 阅读更多 →
全国DEM数据TIF分幅处理指南:从解压到坡度分析的全流程解析

全国DEM数据TIF分幅处理指南:从解压到坡度分析的全流程解析

简介&#xff1a;全国DEM数据TIF数据.zip提供覆盖全国范围的数字高程模型栅格数据&#xff0c;面向地理信息、遥感及相关地学研究人员与工程技术人员&#xff0c;可用于地形特征提取、流域划分、土地利用适宜性评价以及三维地形可视化等多种分析场景。压缩包内共有七个文件&…

2026/10/20 2:27:30 阅读更多 →
Unity WebGL播放海康监控:AVProVideo方案与踩坑实录

Unity WebGL播放海康监控:AVProVideo方案与踩坑实录

简介&#xff1a;面向需要在Unity WebGL平台中集成海康摄像头实时画面的开发人员&#xff0c;这份示例源码演示了基于AVProVideo插件播放监控视频流的完整方案。针对WebGL环境常见的跨域与兼容性问题&#xff0c;作者重写了Http请求模块&#xff0c;并附上AVProVideo无水印版本…

2026/10/20 2:27:30 阅读更多 →

最新新闻

Ubuntu下PostgreSQL安装配置与Navicat连接指南

Ubuntu下PostgreSQL安装配置与Navicat连接指南

做后端开发或者数据相关工作的朋友&#xff0c;应该都经历过这么一幕&#xff1a;好不容易在 Ubuntu 上把 PostgreSQL 装好了&#xff0c;却发现命令行操作没问题&#xff0c;客户端工具却怎么都连不上&#xff1b;或者反过来&#xff0c;Navicat 装好了&#xff0c;连接时报一…

2026/10/20 17:33:06 阅读更多 →
uniCloud Serverless 实战:英语学习小程序全链路开发

uniCloud Serverless 实战:英语学习小程序全链路开发

简介&#xff1a;这是一套面向英语学习者与小程序开发者的完整项目源码&#xff0c;基于uniapp与uniCloud Serverless架构构建&#xff0c;可直接在微信环境中运行。项目集生词本记录、拍照识别单词、内置词典查询、AI语音翻译、跟读打分评测于一体&#xff0c;并针对考研人群补…

2026/10/20 17:33:06 阅读更多 →
WiFi指纹室内定位实战:从原理到Demo搭建

WiFi指纹室内定位实战:从原理到Demo搭建

1. 室内定位的现实困境与WiFi方案的破局点实话实说&#xff0c;第一次接手“基于WiFi的室内定位”这个方向时&#xff0c;我心里是打鼓的。日常大家用GPS导航已经习惯了&#xff0c;掏出手机就能知道自己在哪&#xff0c;但一旦走进大型商场、地下车库、机场航站楼&#xff0c;…

2026/10/20 17:33:06 阅读更多 →
大模型后端长对话记忆架构全解析:从无状态到有状态

大模型后端长对话记忆架构全解析:从无状态到有状态

做后端的人都有过这种体验&#xff1a;用户和AI助手聊了三十轮&#xff0c;前二十轮里明明已经把需求背景、偏好、禁忌都交代清楚了&#xff0c;结果聊到第三十一轮&#xff0c;AI突然反问一句“您刚刚提到的那个项目背景&#xff0c;方便再描述一下吗&#xff1f;”当场血压就…

2026/10/20 17:33:06 阅读更多 →
D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

最近不少做视觉入门的朋友卡在第一关——把D435相机的画面真正“看”到屏幕上。很多人买了一台Intel D435深度相机&#xff0c;兴冲冲插上USB线&#xff0c;结果CPU占用飙高、程序报错、画面黑屏&#xff0c;折腾一晚上就劝退了。其实用D435搭配OpenCV显示彩色图和深度图&#…

2026/10/20 17:33:06 阅读更多 →
自考论文降AI率实战:智能体改写与人工复核全流程

自考论文降AI率实战:智能体改写与人工复核全流程

自考论文写到提交前&#xff0c;最让人血压升高的往往不是查重率&#xff0c;而是学校突然要求提交一份AI检测报告。A同学就是这种状况&#xff1a;论文初稿在查重系统里已经降到合格线&#xff0c;结果学院的通知里多了一句“AI疑似率不得超过20%”。他拿自己的稿子去测了一下…

2026/10/20 17:32:04 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →