Just a little bit of background. We are moving away from storing our user passwords hashed in a database to authenticating directly against Active Directory. Unfortunately we have so many internal applications that updating every application is not an option at this point.
So instead of requiring all our developers to change their code, I thought I could just build a .NET CLR Assembly for SQL and just modify the existing authentication stored procedure to validate the users password against our domain controllers instead of a table.
This has been successful in our initial testing, except when the domain controller cannot be communicated with for whatever reason (shutdown, restart, network, etc). When this happens we get timeouts. I added a try/catch and while loop to just skip to the next domain controller and attempt to authenticate against it, but it still takes the first attempt well over 20 seconds to fail before it tries to authenticate against the next server. Since this is a SQL assembly/function and is being called in a stored procedure 20 seconds is way too long.
What is the best approach to modify my code and fail the authentication attempt against a server and try the next server, say, after 3 seconds? Threading somehow?
Imports System
Imports System.Data
Imports System.Collections
Imports System.Text
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.Runtime.InteropServices
Imports System.DirectoryServices.AccountManagement
Partial Public Class UserDefinedFunctions
<SqlFunction(Name:="ValidCredentials")>
Public Shared Function ValidCredentials(ByVal Domain As SqlString, ByVal Username As SqlString, ByVal Password As SqlString) As SqlBoolean
'Validates the username and password are correct against the domain.
Dim ServerNum As Integer = 1
Dim Success As Boolean = False
Dim ValidCreds As SqlBoolean = False
'Loop through all 4 domain controllers to attempt authentication against each of them.
While Success = False And ServerNum <= 4
Try
Dim pc As PrincipalContext
If Domain = "employees" Then
pc = New PrincipalContext(ContextType.Domain, "DC-E-0" & ServerNum.ToString & ".employees.ourdomain.loc", "EMPLOYEES\BindAcct", "PASS")
Else
pc = New PrincipalContext(ContextType.Domain, "DC-S-0" & ServerNum.ToString & ".students.ourdomain.loc", "STUDENTS\BindAcct", "PASS")
End If
ValidCreds = pc.ValidateCredentials(Username, Password)
Success = True
Return ValidCreds
Catch ex As Exception
ValidCreds = False
Success = False
ServerNum += 1
End Try
End While
Return ValidCreds
End Function
End Class
You can set timeout value for your function on sql server 2008 or above with the following lines of code. Just put it on the top of your block:
SET LOCK_TIMEOUT 3000;
GO
The value of the timeout is expressed in milliseconds
.
According to MS Documentation :
When a wait for a lock exceeds the time-out value, an error is returned.
That will cause that your function takes less than 3 seconds or fail. Then you should catch the exception and rollback if needed.
You can do this with a separate thread:
The code is in C#, but it should be straightforward to convert.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.