C# Winforms如何实现登录表单每24小时仅验证一次
C# WinForm 24小时免登实现方案(服务端优先计时)
核心逻辑
优先采用服务端计时天然支持关机后时间正常累计,完全规避客户端修改本地时间绕过有效期的问题,整体实现分为服务端接口改造和客户端逻辑调整两部分。
服务端改造点
- 原有登录接口验证账号密码通过后,生成唯一授权Token,同时记录该Token的过期时间戳=服务器当前时间戳+86400(24小时秒数),将Token、过期时间、对应用户信息存储在服务端后,把Token和过期时间返回给客户端
- 新增Token有效性校验接口,入参为客户端上传的历史Token,返回两种状态:
有效/无效,有效状态可同步返回当前绑定的用户基本信息,无效状态对应Token过期、非法或者已被销毁
客户端实现步骤
- 调整程序启动入口逻辑,不要直接加载登录窗体,优先执行免登校验
- 本地用加密方式存储历史授权Token,禁止明文存储,推荐用Windows DPAPI加密后存到系统配置文件或用户目录下的独立配置文件中
- 启动校验流程:
- 第一步:读取本地存储的Token,不存在直接弹出登录窗体
- 第二步:存在Token则调用服务端校验接口,网络异常场景可根据业务需求选择直接走登录流程,或补充本地时间校验作为降级方案
- 第三步:接口返回Token有效,直接加载主窗体,同步传入接口返回的用户信息即可
- 第四步:接口返回Token无效,清空本地存储的失效Token,弹出登录窗体
- 手动登录成功后,将接口返回的新Token加密存储到本地,直接跳转主窗体
关键代码示例
1. Token加密存储工具类
using System; using System.Configuration; using System.Security.Cryptography; using System.Text; public static class AuthStorageHelper { // DPAPI加密存储Token,仅当前Windows用户可解密 public static void SaveToken(string token) { byte[] plainText = Encoding.UTF8.GetBytes(token); byte[] encryptedData = ProtectedData.Protect(plainText, null, DataProtectionScope.CurrentUser); Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); if (config.AppSettings.Settings["AuthToken"] == null) config.AppSettings.Settings.Add("AuthToken", Convert.ToBase64String(encryptedData)); else config.AppSettings.Settings["AuthToken"].Value = Convert.ToBase64String(encryptedData); config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); } // 读取解密Token public static string GetToken() { string encryptedStr = ConfigurationManager.AppSettings["AuthToken"]; if (string.IsNullOrEmpty(encryptedStr)) return null; try { byte[] encryptedData = Convert.FromBase64String(encryptedStr); byte[] plainText = ProtectedData.Unprotect(encryptedData, null, DataProtectionScope.CurrentUser); return Encoding.UTF8.GetString(plainText); } catch { ClearToken(); return null; } } // 清空失效Token public static void ClearToken() { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); if (config.AppSettings.Settings["AuthToken"] != null) { config.AppSettings.Settings.Remove("AuthToken"); config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); } } }
2. 程序入口逻辑调整(Program.cs)
using System; using System.Windows.Forms; static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); string localToken = AuthStorageHelper.GetToken(); bool autoLoginPassed = false; if (!string.IsNullOrEmpty(localToken)) { try { // 替换为实际的服务端校验接口调用逻辑 var validateResult = YourApiService.ValidateToken(localToken); if (validateResult.IsValid) { Application.Run(new MainForm(validateResult.UserInfo)); autoLoginPassed = true; } else { AuthStorageHelper.ClearToken(); } } catch { // 网络异常场景处理,需离线可用的话可在此补充本地时间校验逻辑 MessageBox.Show("网络连接异常,请检查网络后重试"); } } if (!autoLoginPassed) { LoginForm loginForm = new LoginForm(); if (loginForm.ShowDialog() == DialogResult.OK) { AuthStorageHelper.SaveToken(loginForm.LoginResponse.Token); Application.Run(new MainForm(loginForm.LoginResponse.UserInfo)); } } } }
注意事项
- 服务端校验优先级永远高于本地校验,不要完全依赖本地时间判断有效期
- 服务端需定期清理过期Token,避免无效数据占用存储空间
- 本地时间校验仅可作为无网络场景的降级方案,存在被用户修改本地时间绕过的风险
内容的提问来源于stack exchange,提问作者Custom
相关产品推荐
相关产品推荐

