Я разрабатываю приложение в среде нашей небольшой компании. Это наша установка в двух словах:
Я хочу иметь возможность аутентифицировать пользователей в приложении без необходимости вводить их логин AD, просто основываясь на том факте, что они вошли в свой компьютер. Затем я хочу иметь возможность предоставлять пользователям разрешения в приложении по своему усмотрению.
Вот что я придумал:
tblEmployees:
Когда любой пользователь запускает приложение MS Access, при запуске оно делает что-то вроде этого:
Private Sub Form_Load()
Dim rsUser As Recordset
Dim intEmployeeID As Integer
Dim strEmployeeName As String
Set rsUser = CurrentDb.OpenRecordset("SELECT * FROM tblEmployees", dbOpenSnapshot, dbReadOnly)
rsUser.FindFirst "ADLogin='" & Environ("USERDOMAIN") & "\" & Environ("USERNAME") & "'"
If rsUser.NoMatch Then
' User does not have access to the application
rsUser.Close
Set rsUser = Nothing
Application.Quit
Else
' User with this AD Login has been found and that means that he does have access
intEmployeeID = rsUser("EmployeeID")
strEmployeeName = rsUser("EmployeeName")
TempVars("EmployeeID") = intEmployeeID
TempVars("EmployeeName") = strEmployeeName
DoCmd.OpenForm "frm_MainMenu"
Forms!frm_MainMenu.Requery
DoCmd.Close acForm, Me.Name, acSaveYes
Forms!frm_MainMenu!txtLoggedUser = TempVars("EmployeeName")
End If
rsUser.Close
Set rsUser = Nothing
End Sub
Затем я буду использовать TempVars("EmployeeID") во всем приложении, чтобы сделать формы и кнопки доступными и так далее.
Мой вопрос: это хорошая практика? Это безопасно? Возможно, есть лучший способ сделать это? Спасибо за любые советы.
Идея здесь состоит в том, чтобы обеспечить безопасность как на переднем, так и на заднем конце. Внутренняя безопасность не позволит мне предоставить пользователю доступ к функциям Access, а только к самим данным. Простите, а что вы подразумеваете под "пользователь отключает VBA"?
В реестре вы можете отключить VBA, а затем открыть базы данных Access. Любой содержащийся в них VBA не будет работать.
Я могу отключить доступ к реестру через GPO
Затем они могут изменить реестр через VBA. Или переместите его на другой компьютер с помощью USB-накопителей/почты/и т. д., затем выньте часть VBA. Вам действительно нужно позаботиться о защите данных на стороне сервера, в VBA он всегда будет в лучшем случае частично безопасным (и маршрут environ известен как действительно небезопасный, поскольку эти переменные легко модифицируются)
Прежде всего позвольте мне начать с того, что Access не является безопасным, но это зависит от знаний ваших коллег и от того, насколько далеко они готовы зайти.
Что касается вашего решения, я считаю, что нет необходимости загружать всю таблицу в набор записей, а затем пытаться найти нужную запись, когда вы можете отфильтровать ее непосредственно в источнике. Кроме того, конкатенация строк (в большинстве случаев) является плохой практикой, вместо этого создайте запрос (поскольку его использование будет частым) и передайте входные данные в качестве параметров.
См. пример:
Запрос (ваши поля могут отличаться, но вы поняли идею)
PARAMETERS [Domain] Text (50), [Username] Text (50);
SELECT *
FROM T
WHERE T.Domain=[Domain] AND T.Username=[Username];
Вы также можете создать временный запрос из кода.
Const SQL As String = "The SQL command above"
'No query name means it's temporary and will be deleted once the method runs.
Dim q As DAO.QueryDef
Set q = CurrentDb().CreateQueryDef("", SQL)
Чтобы вызвать его и проверить зарегистрированного пользователя:
Dim q As DAO.QueryDef
Dim r As DAO.Recordset
Set q = CurrentDb().QueryDefs("YourQueryName")
'Using the Environ() method.
q.Parameters("[Domain]").Value = Environ("USERDOMAIN")
q.Parameters("[Username]").Value = Environ("USERNAME")
'OR alternative method to get the username/domain from @Andre in comments
'This is more secure than the Environ() method.
With CreateObject("WScript.Network")
q.Parameters("[Domain]").Value = .UserDomain
q.Parameters("[Username]").Value = .UserName
End With
'read-only, no need for changes.
Set r = q.OpenRecordset(dbOpenSnapshot)
'Not found
If r.EOF Then
DoCmd.Quit acQuitPrompt
Exit Sub
End If
'Found
'The recordset now contains whatever fields the query selected.
'Do what needs to be done.
'...
'Clean up
If Not r Is Nothing Then r.Close
If Not q Is Nothing Then q.Close
Наконец, я бы изменил переменную intEmployeeID
на тип Long
и добавил бы в метод обработку ошибок.
Как минимум, вы можете использовать не Environ
для получения имени пользователя и домена, а что-то более безопасное, как указано здесь.
Конечно, но лично я считаю это ненужным внутри домена компании. Я могу ошибаться конечно.
Вам не нужен вызов API, см. stackoverflow.com/a/32565953/3820271
Спасибо, Костас, это логично. Однако могу я спросить, в чем преимущество запроса Access по сравнению с получением данных через набор записей? Я пытаюсь сохранить интерфейс как можно более чистым, в настоящее время у меня нет локальных запросов. На данный момент все обрабатывается через представления SQL Server. Заранее спасибо за разъяснения :)
Конкатенация строк и параметры, и вы сразу возвращаете только нужную запись (или ничего). Вам не нужно иметь статический запрос, но создайте его на лету из кода. Смотрите это: stackoverflow.com/questions/74078962/…
@ThomassoCZ Добавлен пример временного запроса.
Определенно не безопасно (представьте, что произойдет, если пользователь отключит VBA). Почему бы просто не обрабатывать аутентификацию на бэкэнде SQL-сервера?\