我们正在使用Oracle数据库中的LDAP进行用户身份验证。但是,身份验证结果始终为-16,在某些示例中,我已经看到将其转换为无效凭据。我已经使用DBMS_LDAP.search_s搜索了Active目录,并确保输入的DN是正确的。有趣的是DBMS_LDAP.SIMPLE_BIND_S,密码不正确,绑定不好。但是,当使用DBMS_LDAP_UTL.AUTHENTICATE_USER尝试相同的密码,错误的DN时,它始终返回-16。以下是身份验证示例:
DECLARE
ldap_host VARCHAR2(256);
ldap_port PLS_INTEGER;
ldap_user VARCHAR2(256);
ldap_passwd VARCHAR2(256);
ldap_base VARCHAR2(256);
retval PLS_INTEGER;
my_session DBMS_LDAP.session;
subscriber_handle DBMS_LDAP_UTL.HANDLE;
sub_type PLS_INTEGER;
subscriber_id VARCHAR2(2000);
my_pset_coll DBMS_LDAP_UTL.PROPERTY_SET_COLLECTION;
my_property_names DBMS_LDAP.STRING_COLLECTION;
my_property_values DBMS_LDAP.STRING_COLLECTION;
user_handle DBMS_LDAP_UTL.HANDLE;
user_id VARCHAR2(2000);
user_type PLS_INTEGER;
user_password VARCHAR2(2000);
my_mod_pset DBMS_LDAP_UTL.MOD_PROPERTY_SET;
my_attrs DBMS_LDAP.STRING_COLLECTION;
locate raw(4000);
BEGIN
-- Please customize the following variables as needed
ldap_host := 'host' ;
ldap_port := 389;
ldap_user := 'CN=UserName1,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
ldap_passwd := 'SamePassword';
sub_type := DBMS_LDAP_UTL.TYPE_DN;
subscriber_id := 'OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
user_type := DBMS_LDAP_UTL.TYPE_DN;
user_id := 'CN=UserName2,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';;
user_password := 'SamePassword';
-- Choosing exceptions to be raised by DBMS_LDAP library.
DBMS_LDAP.USE_EXCEPTION := TRUE;
-----------------------------------------------
-- Connect to the LDAP server
-- and obtain and ld session.
-----------------------------------------------
my_session := DBMS_LDAP.init(ldap_host,ldap_port);
-----------------------------------------------
-- Bind to the directory
--
-----------------------------------------------
retval := DBMS_LDAP.simple_bind_s(my_session,
ldap_user,
ldap_passwd);
---------------------------------------------------------------------
-- Create Subscriber Handle
--
---------------------------------------------------------------------
retval := DBMS_LDAP_UTL.create_subscriber_handle(subscriber_handle,
sub_type,
subscriber_id);
IF retval != DBMS_LDAP_UTL.SUCCESS THEN
-- Handle Errors
DBMS_OUTPUT.PUT_LINE('create_subscriber_handle returns : ' || to_char(retval));
END IF;
---------------------------------------------------------------------
-- Create User Handle
--
---------------------------------------------------------------------
retval := DBMS_LDAP_UTL.create_user_handle(user_handle,user_type,user_id);
IF retval != DBMS_LDAP_UTL.SUCCESS THEN
-- Handle Errors
DBMS_OUTPUT.PUT_LINE('create_user_handle returns : ' || to_char(retval));
END IF;
---------------------------------------------------------------------
dbms_output.put_line(dbms_ldap_utl.locate_subscriber_for_user(
ld => my_session,
user_handle => user_handle,
subscriber_handle => locate));
dbms_output.put_line(DBMS_LDAP_UTL.SUCCESS);
---------------------------------------------------------------------
-- Set user handle properties
-- (link subscriber to user )
---------------------------------------------------------------------
retval := DBMS_LDAP_UTL.set_user_handle_properties(user_handle,
DBMS_LDAP_UTL.SUBSCRIBER_HANDLE,
subscriber_handle);
IF retval != DBMS_LDAP_UTL.SUCCESS THEN
-- Handle Errors
DBMS_OUTPUT.PUT_LINE('set_user_handle_properties returns : ' || to_char(retval));
END IF;
---------------------------------------------------------------------
-- Authenticate User
---------------------------------------------------------------------
retval := DBMS_LDAP_UTL.authenticate_user(my_session,
user_handle,
DBMS_LDAP_UTL.AUTH_SIMPLE,
user_password,
NULL);
IF retval != DBMS_LDAP_UTL.SUCCESS THEN
-- Handle Errors
DBMS_OUTPUT.PUT_LINE('authenticate_user returns : ' || to_char(retval));
END IF;
---------------------------------------------------------------------
-- Free handles
---------------------------------------------------------------------
DBMS_LDAP_UTL.free_handle(subscriber_handle);
DBMS_LDAP_UTL.free_handle(user_handle);
-- unbind from the directory
retval := DBMS_LDAP.unbind_s(my_session);
IF retval != DBMS_LDAP_UTL.SUCCESS THEN
-- Handle Errors
DBMS_OUTPUT.PUT_LINE('unbind_s returns : ' || to_char(retval));
END IF;
-- Handle Exceptions
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(' Error code : ' || TO_CHAR(SQLCODE));
DBMS_OUTPUT.PUT_LINE(' Error Message : ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE(' Exception encountered .. exiting');
END;
/
答案 0 :(得分:2)
如果您只想根据LDAP对用户进行身份验证,则非常简单:
DECLARE
ret NUMBER;
ld DBMS_LDAP.SESSION;
SUCCESS INTEGER;
ldap_host VARCHAR2(100) := 'host';
ldap_port INTEGER := 389;
userDn VARCHAR2(100) := 'OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
userPassword VARCHAR2(100) := 'secret';
BEGIN
DBMS_LDAP.USE_EXCEPTION := TRUE;
ld := DBMS_LDAP.INIT(ldap_host, ldap_port);
DBMS_LDAP.USE_EXCEPTION := FALSE;
SUCCESS := DBMS_LDAP.SIMPLE_BIND_S(ld, userDn, userPassword);
IF SUCCESS = DBMS_LDAP.SUCCESS AND userPassword IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE('Password is valid');
ELSE
DBMS_OUTPUT.PUT_LINE('Wrong password');
END IF;
ret := DBMS_LDAP.UNBIND_S(ld);
END;
如果您想查询给定用户的属性,可以查看此示例:
DECLARE
SUBTYPE T_USER_ACCOUNT_CONTROL IS INTEGER;
-- see https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro
SKRIPT CONSTANT T_USER_ACCOUNT_CONTROL := 1;
ACCOUNTDISABLE CONSTANT T_USER_ACCOUNT_CONTROL := 2;
HOMEDIR_REQUIRED CONSTANT T_USER_ACCOUNT_CONTROL := 8;
LOCKOUT CONSTANT T_USER_ACCOUNT_CONTROL := 16;
PASSWD_NOTREQD CONSTANT T_USER_ACCOUNT_CONTROL := 32;
PASSWD_CANT_CHANGE CONSTANT T_USER_ACCOUNT_CONTROL := 64;
ENCRYPTED_TEXT_PWD_ALLOWED CONSTANT T_USER_ACCOUNT_CONTROL := 128;
TEMP_DUPLICATE_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 256;
NORMAL_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 512;
INTERDOMAIN_TRUST_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 2048;
WORKSTATION_TRUST_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 4096;
SERVER_TRUST_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 8192;
DONT_EXPIRE_PASSWORD CONSTANT T_USER_ACCOUNT_CONTROL := 65536;
MNS_LOGON_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 131072;
SMARTCARD_REQUIRED CONSTANT T_USER_ACCOUNT_CONTROL := 262144;
TRUSTED_FOR_DELEGATION CONSTANT T_USER_ACCOUNT_CONTROL := 524288;
NOT_DELEGATED CONSTANT T_USER_ACCOUNT_CONTROL := 1048576;
USE_DES_KEY_ONLY CONSTANT T_USER_ACCOUNT_CONTROL := 2097152;
DONT_REQ_PREAUTH CONSTANT T_USER_ACCOUNT_CONTROL := 4194304;
PASSWORD_EXPIRED CONSTANT T_USER_ACCOUNT_CONTROL := 8388608;
TRUSTED_TO_AUTH_FOR_DELEGATION CONSTANT T_USER_ACCOUNT_CONTROL := 16777216;
PARTIAL_SECRETS_ACCOUNT CONSTANT T_USER_ACCOUNT_CONTROL := 67108864;
LDAP_USER CONSTANT VARCHAR2(255) := 'CN=UserName1,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
LDAP_PASSWORD CONSTANT VARCHAR2(30) := 'secret';
LDAP_SERVER CONSTANT VARCHAR2(30) := 'host';
LDAP_PORT INTEGER := 389;
userDn VARCHAR2(100) := 'CN=UserName2,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
ld DBMS_LDAP.SESSION;
ret NUMBER;
ldapEntry DBMS_LDAP.MESSAGE;
attrs DBMS_LDAP.STRING_COLLECTION;
ldapMessage DBMS_LDAP.MESSAGE;
attribName VARCHAR2(256);
berEelement DBMS_LDAP.BER_ELEMENT;
info DBMS_LDAP.STRING_COLLECTION;
pwdLastSet TIMESTAMP;
expireDate TIMESTAMP
BEGIN
attrs(1) := 'displayName';
attrs(2) := 'userAccountControl';
attrs(3) := 'pwdLastSet';
ld := DBMS_LDAP.INIT(LDAP_SERVER, LDAP_PORT);
ret := DBMS_LDAP.SIMPLE_BIND_S(ld, LDAP_USER, LDAP_PASSWORD);
DBMS_LDAP.USE_EXCEPTION := FALSE;
ret := DBMS_LDAP.SEARCH_S(
ld => ld,
base => 'DC=pan,DC=int',
SCOPE => DBMS_LDAP.SCOPE_SUBTREE,
FILTER => '&(objectCategory=user)(distinguishedName='||userDn||')',
attrs => attrs,
attronly => 0,
res => ldapMessage);
ldapEntry := DBMS_LDAP.FIRST_ENTRY(ld, ldapMessage);
IF ldapEntry IS NULL THEN
DBMS_OUTPUT.PUT_LINE('User "'||userDn||'" does not exist');
ELSE
WHILE ldapEntry IS NOT NULL LOOP
attribName := DBMS_LDAP.FIRST_ATTRIBUTE(ld, ldapEntry, berEelement);
WHILE attribName IS NOT NULL LOOP
info := DBMS_LDAP.GET_VALUES(ld, ldapEntry, attribName);
CASE attribName
WHEN 'displayName' THEN
DBMS_OUTPUT.PUT_LINE('Display Name = ' || info(info.FIRST));
WHEN 'userAccountControl' THEN
IF SIGN(BITAND(ACCOUNTDISABLE, info(info.FIRST))) = 1 THEN
DBMS_OUTPUT.PUT_LINE('Account is disabled');
END IF;
IF SIGN(BITAND(PASSWORD_EXPIRED, info(info.FIRST))) = 1 THEN
DBMS_OUTPUT.PUT_LINE('Password is expired');
END IF;
WHEN 'pwdLastSet' THEN
pwdLastSet := (TIMESTAMP '1601-01-01 00:00:00 UTC' + info(info.FIRST)/1000/1000/10/60/60/24 * INTERVAL '1' DAY) AT LOCAL;
DBMS_OUTPUT.PUT_LINE('Password was changed at ' || TO_CHAR(pwdLastSet, 'yyyy-mm-dd hh24:mi:ss'));
-- Password life time is not available in LDAP. Check your company policy and calculate expire date accordingly, for example:
expireDate := pwdLastSet + INTERVAL '6' MONTH;
DBMS_OUTPUT.PUT_LINE('Your password will expire at ' || TO_CHAR(expireDate, 'yyyy-mm-dd hh24:mi:ss'));
END CASE;
attribName := DBMS_LDAP.NEXT_ATTRIBUTE(ld, ldapEntry, berEelement);
END LOOP;
ldapEntry := DBMS_LDAP.NEXT_ENTRY(ld, ldapEntry);
END LOOP;
DBMS_LDAP.BER_FREE(berEelement, freebuf => 0);
END IF;
ret := DBMS_LDAP.MSGFREE(ldapMessage);
ret := DBMS_LDAP.UNBIND_S(ld);
end;
<强>更新强>
当我说&#34时,我犯了一个错误;密码生命时间在LDAP&#34;中不可用。您可以这样查询:
BEGIN
dn := 'DC=pan,DC=int';
attrs(1) := 'maxPwdAge';
ld := DBMS_LDAP.INIT(LDAP_SERVER, LDAP_PORT);
ret := DBMS_LDAP.SIMPLE_BIND_S(ld, LDAP_USER, LDAP_PASSWORD);
DBMS_LDAP.USE_EXCEPTION := FALSE;
ret := DBMS_LDAP.SEARCH_S(
ld => ld,
base => 'DC=pan,DC=int',
SCOPE => DBMS_LDAP.SCOPE_SUBTREE,
FILTER => '&(objectCategory=domain)(distinguishedName='||dn||')',
attrs => attrs,
attronly => 0,
res => ldapMessage);
-- You don't need a loop because you have only one single entry and one single attribute
ldapEntry := DBMS_LDAP.FIRST_ENTRY(ld, ldapMessage);
attribName := DBMS_LDAP.FIRST_ATTRIBUTE(ld, ldapEntry, berEelement);
info := DBMS_LDAP.GET_VALUES(ld, ldapEntry, attribName);
DBMS_OUTPUT.PUT_LINE('Password life time = ' || -info(info.FIRST)/1000/1000/10/60/60/24 || ' days');
DBMS_LDAP.BER_FREE(berEelement, freebuf => 0);
ret := DBMS_LDAP.MSGFREE(ldapMessage);
ret := DBMS_LDAP.UNBIND_S(ld);
END;