Hi,大家好!
今天我们来讲一下如何获取Windows账号,实现单点登录。
在构建企业级 Access 应用程序(C/S 架构)时,开发者往往面临一个两难选择:是自己设计一套“用户表+密码字段”的登录系统,还是直接利用现有的企业基础设施?
答案显而易见。在域环境(Active Directory)下,利用 SSO(单点登录) 是标准且最佳的实践。
01
什么是SSO
SSO (Single Sign-On),即单点登录。它的核心定义是:在多个应用系统中,用户只需要登录一次,就可以访问所有相互信任的应用系统。
在我们的 Access 开发场景中,指 Windows 集成认证 (Integrated Windows Authentication, IWA)。
举个栗子简单的理解一下:
- 传统模式:你去公司大楼(Windows),保安查了一次证件。进办公室(Access系统)时,前台又要你背一遍身份证号和密码。
- SSO 模式:你刷卡进了公司大楼(Windows 登录成功),你的工牌(Token)就是你的通行证。当你走进办公室(打开 Access)时,系统只看你的工牌,确认是“自己人”,直接放行。
02
为什么要用SSO
很多开发者觉得:“我自己写个密码判断 If txtPassword = "123456" 很简单啊,为什么要搞这么复杂?”
这是一个典型的误区。引入 SSO 不仅仅是为了“少输一次密码”,是为了解决企业开发中的几个问题:
1. 安全性
Access 并不是一个专业的安全工具。
存储风险:如果你在 Access 表中存储密码(即使是 MD5 加密),一旦 .accdb 文件被拷贝,有无数种工具可以暴力破解。
2. 运维成本:终结“忘记密码”的工单
IT 部门的就不会再有软件系统“重置密码“的工单。只要用户能登录 Windows,他就能登录 Access。你不需要维护一套独立的密码库,也不需要处理密码找回请求。
3. 权限生命周期管理
当员工离职时,IT 部门会禁用其 Windows 域账号。如果 Access 有独立的账户体系,管理员忘记在 Access 里禁用该员工,SSO 的优势:域账号一封禁,所有依赖 SSO 的系统(包括你的 Access)瞬间全部拒绝访问。
03
核心代码
新建标准模块 mod_Auth,代码如下:
Option Compare DatabaseOption Explicit' ---------------------------------------------------------' API 声明:兼容 VBA7 (x64) 及旧版本 (x86)' ---------------------------------------------------------#If VBA7 Then Private Declare PtrSafe Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" _ (ByVal lpBuffer As String, nSize As Long) As Long#Else Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" _ (ByVal lpBuffer As String, nSize As Long) As Long#End If' ---------------------------------------------------------' 函数:GetSystemUser' 描述:通过 API 获取当前 Windows 登录账户名' 原理:读取当前线程的安全令牌,而非环境变量' ---------------------------------------------------------Public Function GetSystemUser() As String Dim strBuffer As String Dim lngSize As Long Dim lngResult As Long ' 初始化缓冲区:API 需要预先分配内存空间 ' 255 字符通常足以容纳 Windows 用户名 strBuffer = String(255, vbNullChar) lngSize = Len(strBuffer) ' 调用 API ' lpBuffer: 接收用户名的缓冲区 ' nSize: 传入缓冲区长度,返回实际用户名长度 lngResult = GetUserName(strBuffer, lngSize) If lngResult <> 0 Then ' API 返回成功 (非0) ' 注意:lngSize 返回的是包含 Null 结尾的长度,需要 -1 GetSystemUser = Left$(strBuffer, lngSize - 1) Else ' API 调用失败 (通常极少发生,除非系统底层异常) GetSystemUser = "Unknown" ' 实际生产中建议在此处记录 Err.LastDllError End IfEnd Function04
架构设计
获取用户名只是第一步,完整的权限控制需要数据库层面的配合。建议采用 RBAC (基于角色的访问控制) 模型。这里我们简单的来描述一下。
1. 数据库 Schema 设计
在后端数据库(或本地表)创建 sys_Users 表:
User_ID (PK, AutoNumber)
Win_Account (String, Indexed, Unique) - 存储 Windows 登录名
Role_Code (String) - 例如: 'ADMIN', 'VIEWER'
Is_Active (Boolean) - 软删除标记
2. 启动挂载逻辑
利用 Access 的启动窗体(Splash Screen)作为控制器。
在启动窗体 frm_Splash 的 Form_Load 事件中:
Private Sub Form_Load() On Error GoTo ErrorHandler Dim strCurrentUser As String Dim strRole As String ' 1. 获取系统标识 (SSO 核心步骤) strCurrentUser = GetSystemUser() ' 2. 数据库鉴权 (Authorization) ' 认证(Authentication)由 Windows 完成,Access 只负责授权(Authorization) strRole = Nz(DLookup("Role_Code", "sys_Users", _ "Win_Account='" & strCurrentUser & "' AND Is_Active=True"), "") ' 3. 逻辑分发 If Len(strRole) = 0 Then LogAccessAttempt strCurrentUser, "FAILED" ' 记录审计日志 MsgBox "Access Denied: User [" & strCurrentUser & "] not authorized.", vbCritical Application.Quit acQuitSaveNone Else ' 初始化全局会话变量 TempVars.Add "Current_User", strCurrentUser TempVars.Add "Current_Role", strRole LogAccessAttempt strCurrentUser, "SUCCESS" ' 根据角色跳转 DoCmd.OpenForm "frm_MainDashboard" End If Exit SubErrorHandler: MsgBox "System Error: " & Err.Description, vbCritical Application.QuitEnd Sub05
总结
通过引入 SSO,我们将 Access 的身份验证委托给了最值得信任的操作系统。这不仅让代码更简洁(无需编写复杂的密码哈希、加盐逻辑),更让整个系统的安全架构提升到了企业级标准。
喜欢这篇文章吗?欢迎点赞、在看、转发,让更多 Access 爱好者看到!

