1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
|
UserPrincipalName:指定要由客户端进行身份验证的服务的用户主体名称 (UPN)。一个用户帐户名(有时称为“用户登录名”)和一个域名(标识用户帐户所在的域), 这是登录到Windows域的标准用法。格式是: xiaowen@azureyun.com (类电子邮件地址)。
SamAccountName:在AD属性AMAccountName中,存储帐户登录名或用户对象,实际上是命名符号“Domain\LogonName”中使用的旧NetBIOS表单, 该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致,即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。
public UserDetails call() throws AuthenticationException, NamingException { DirContext context; boolean anonymousBind = false; // did we bind anonymously?
// LDAP treats empty password as anonymous bind, so we need to reject it if (StringUtils.isEmpty(password)) { throw new BadCredentialsException("Empty password"); }
String userPrincipalName = getPrincipalName(username, domain.getName()); // mage@company.com String samAccountName = userPrincipalName.substring(0, userPrincipalName.indexOf('@')); // mage
// 匿名绑定ad域控 try { context = descriptor.bind(userPrincipalName, password, ldapServers, props, domain.getTlsConfiguration()); // userPrincipalName=mage@company.com // password=mypassword. // ldapServers = arraylist, 10.0.0.60:389 // props=hashtable, com.sun.jndi.ldap.connect.timeout -> 30000, com.sun.jndi.ldap.read.timeout -> 60000 // tlsConfiguration
} catch (BadCredentialsException e) { if (anonymousBind) // in my observation, if we attempt an anonymous bind and AD doesn't allow it, it still passes the bind method // and only fail later when we actually do a query. So perhaps this is a dead path, but I'm leaving it here // anyway as a precaution. throw new UserMayOrMayNotExistException("Unable to retrieve the user information without bind DN/password configured"); throw e; }
try { // locate this user's record final String domainDN = toDC(domain.getName()); //DC=company,DC=com
Attributes user = new LDAPSearchBuilder(context, domainDN).subTreeScope().searchOne("(& (userPrincipalName={0})(objectCategory=user))", userPrincipalName);
Object dnObject = user.get(DN_FORMATTED).get(); // CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com
String dn = dnObject.toString(); // CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com LdapName ldapName = new LdapName(dn); //CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com String dnFormatted = ldapName.toString();//CN=xxx,OU=xxxx,OU=,OU=,OU=,DC=company,DC=com
Set<GrantedAuthority> groups = resolveGroups(domainDN, dnFormatted, context);// 就是把 域账号所属于的组都搞过来. groups.add(SecurityRealm.AUTHENTICATED_AUTHORITY);
cacheMiss[0] = new ActiveDirectoryUserDetail(username, password, true, true, true, true, groups.toArray(new GrantedAuthority[0]), getStringAttribute(user, "displayName"), getStringAttribute(user, "mail"), getStringAttribute(user, "telephoneNumber") ); return cacheMiss[0]; } catch (NamingException e) { if (activeDirectoryInternalUser != null) { throw e; } if (anonymousBind && e.getMessage().contains("successful bind must be completed") && e.getMessage().contains("000004DC")) { // sometimes (or always?) anonymous bind itself will succeed but the actual query will fail. // see JENKINS-12619. On my AD the error code is DSID-0C0906DC throw new UserMayOrMayNotExistException("Unable to retrieve the user information without bind DN/password configured"); } if (anonymousBind && e.getMessage().contains("Operation unavailable without authentication") && e.getMessage().contains("00002020")) { // sometimes (or always?) anonymous bind itself will succeed but the actual query will fail. // see JENKINS-47133 String msg = String.format("Server doesn't allow to retrieve the information for user `%s` without bind DN/password configured", username); LOGGER.log(Level.WARNING, msg); throw new UserMayOrMayNotExistException(msg); }
LOGGER.log(Level.WARNING, String.format("Failed to retrieve user information for %s", username), e); throw new BadCredentialsException("Failed to retrieve user information for " + username, e); } finally { closeQuietly(context); } }
|