精华内容
下载资源
问答
  • ldap 身份验证的通用步骤

    万次阅读 2017-09-14 11:46:33
    那怎么样才能正确LDAP进行身份验证呢,下面是一个正确而又通用的步骤:  1. 从客户端得到登陆名和密码。注意这里登陆名和密码一开始并没有被用到。  2. 先匿名绑定到LDAP服务器,如果LDAP服务器没
    和利用数据库进行验证类似,LDAP中也是利用登陆名和密码进行验证,LDAP中会定义一个属性password,用来存放用户密码,而登陆名使用较多的都是mail地址。那怎么样才能正确的用LDAP进行身份验证呢,下面是一个正确而又通用的步骤:

           1. 从客户端得到登陆名和密码。注意这里的登陆名和密码一开始并没有被用到。

           2. 先匿名绑定到LDAP服务器,如果LDAP服务器没有启用匿名绑定,一般会提供一个默认的用户,用这个用户进行绑定即可。

           3. 之前输入的登陆名在这里就有用了,当上一步绑定成功以后,需要执行一个搜索,而filter就是用登陆名来构造,形如: "(|(uid=$login)(mail=$login))" ,这里的login就是登陆名。搜索执行完毕后,需要对结果进行判断,如果只返回一个entry,这个就是包含了该用户信息的entry,可以得到该entryDN,后面使用。如果返回不止一个或者没有返回,说明用户名输入有误,应该退出验证并返回错误信息。

           4. 如果能进行到这一步,说明用相应的用户,而上一步执行时得到了用户信息所在的entryDN,这里就需要用这个DN和第一步中得到的password重新绑定LDAP服务器。

           5. 执行完上一步,验证的主要过程就结束了,如果能成功绑定,那么就说明验证成功,如果不行,则应该返回密码错误的信息。

           5大步就是基于LDAP的一个 “两次绑定” 验证方法,下面这个图能更形象的说明这个过程:


            为什么基于LDAP进行验证需要“两次”绑定呢,为什么不能取出password然后和输入进行比较呢,试想一下,如果需要读出密码,服务器上的密码存储要么就不加密,直接可以读出,要么客户就需要知道服务器使用的加密方式,这是不安全,也是不好的,服务器不希望加密方式让客户端知道,客户端也不需要知道这么多。而从实际来看,LDAP服务器对于password属性默认都是不可读的,甚至有的服务器根本就不支持password属性可读,遇到这种情况,也就没有办法取得密码了。

           还有一个问题就是,为什么我们需要第一次绑定?为什么不直接使用DN呢,首先就是关于这个DN,对于一般的客户端程序,其并不知道具体的DN是什么。再者让用户输入DN,给用户带来不便的同时,验证也带来问题,因为如果输入的是个目录树而不是所期望的DN,在进行绑定时有可能会让服务器产生不可预料的错误。

           从上面看来,基于LDAP进行身份验证,最好也是最通用的方法就是 “两次绑定”。

     

    所谓的bind是一个authentication的过程,不要把它想像成“绑定”,既然是认证,就需要一个用户名和密码,openldap中如果出示的用户名和密码错误,服务器会尝试匿名认证,就和匿名ftp一样。当然,在现实配置中可能需要在认证不获得成功就不能做查询操作,这些是在slapd.conf文件中通过设置ACL实现的。

     

    认证所用的用户名和密码为目录树中某个节点的两个属性(用户名和密码),一般情况下,程序会默认使用uiduserPassword属性。写程序进行认证的时候只要提供这个节点的两个属性就可以了。

    展开全文
  • 和利用数据库进行验证类似,LDAP中也是利用登陆名和密码进行验证,LDAP中会定义一个属性password,用来存放用户密码,而...那怎么样才能正确LDAP进行身份验证呢,下面是一个正确而又通用的步骤: 1. ...
    原帖地址:http://blog.csdn.net/xufaxi/article/details/5703516

    和利用数据库进行验证类似,LDAP中也是利用登陆名和密码进行验证,LDAP中会定义一个属性password,用来存放用户密码,而登陆名使用较多的都是mail地址。那怎么样才能正确的用LDAP进行身份验证呢,下面是一个正确而又通用的步骤:

    1. 从客户端得到登陆名和密码。注意这里的登陆名和密码一开始并没有被用到。

    2. 先匿名绑定到LDAP服务器,如果LDAP服务器没有启用匿名绑定,一般会提供一个默认的用户,用这个用户进行绑定即可。

    3. 之前输入的登陆名在这里就有用了,当上一步绑定成功以后,需要执行一个搜索,而filter就是用登陆名来构造,形如: "(|(uid=$login)(mail=$login))" ,这里的login就是登陆名。搜索执行完毕后,需要对结果进行判断,如果只返回一个entry,这个就是包含了该用户信息的entry,可以得到该entry的DN,后面使用。如果返回不止一个或者没有返回,说明用户名输入有误,应该退出验证并返回错误信息。

    4. 如果能进行到这一步,说明用相应的用户,而上一步执行时得到了用户信息所在的entry的DN,这里就需要用这个DN和第一步中得到的password重新绑定LDAP服务器。

    5. 执行完上一步,验证的主要过程就结束了,如果能成功绑定,那么就说明验证成功,如果不行,则应该返回密码错误的信息。

    这5大步就是基于LDAP的一个 “两次绑定” 验证方法,下面这个图能更形象的说明这个过程:

    为什么基于LDAP进行验证需要“两次”绑定呢,为什么不能取出password然后和输入进行比较呢,试想一下,如果需要读出密码,服务器上的密码存储要么就不加密,直接可以读出,要么客户就需要知道服务器使用的加密方式,这是不安全,也是不好的,服务器不希望加密方式让客户端知道,客户端也不需要知道这么多。而从实际来看,LDAP服务器对于password属性默认都是不可读的,甚至有的服务器根本就不支持password属性可读,遇到这种情况,也就没有办法取得密码了。

    还有一个问题就是,为什么我们需要第一次绑定?为什么不直接使用DN呢,首先就是关于这个DN,对于一般的客户端程序,其并不知道具体的DN是什么。再者让用户输入DN,给用户带来不便的同时,验证也带来问题,因为如果输入的是个目录树而不是所期望的DN,在进行绑定时有可能会让服务器产生不可预料的错误。

    从上面看来,基于LDAP进行身份验证,最好也是最通用的方法就是 “两次绑定”。

    所谓的bind是一个authentication的过程,不要把它想像成“绑定”,既然是认证,就需要一个用户名和密码,openldap中如果出示的用户名和密码错误,服务器会尝试匿名认证,就和匿名ftp一样。当然,在现实配置中可能需要在认证不获得成功就不能做查询操作,这些是在slapd.conf文件中通过设置ACL实现的。

    认证所用的用户名和密码为目录树中某个节点的两个属性(用户名和密码),一般情况下,程序会默认使用uid和userPassword属性。写程序进行认证的时候只要提供这个节点的两个属性就可以了。
    展开全文
  • ldap 身份验证

    2020-05-29 20:12:23
    下面是一个正确而又通用的步骤: 1.从客户端得到登录名和密码。注意这里登录名和密码一开始是没有被用到。 2.先匿名绑定到LDAP服务器,如果LDAP服务器没有启用匿名绑定,一般会提供一个默认用户,用这个用户...

    和利用数据库进行验证类似,LDAP中也是利用登陆名和密码进行验证,LDAP中会定义一个属性password,用来存放用户密码,而登陆名使用较多的都是邮箱地址。

    下面是一个正确而又通用的步骤:
           1. 从客户端得到登录名和密码。注意这里的登录名和密码一开始是没有被用到的。

           2. 先匿名绑定到LDAP服务器,如果LDAP服务器没有启用匿名绑定,一般会提供一个默认的用户,用这个用户进行绑定即可。

           3. 之前输入的登录名在这里就有用了,当上一步绑定成功以后,需要执行一个搜索,而filter就是用登陆名来构造,例如: "(|(uid=$login)(mail=$login))" ,这里的login就是登录名。搜索执行完毕后,需要对结果进行判断,如果只返回一个entry,这个就是包含了该用户信息的entry,可以得到该entry的DN,后面使用。如果返回不止一个或者没有返回,说明用户名输入有误,应该退出验证并返回错误信息。

           4. 如果能进行到这一步,说明用相应的用户,而上一步执行时得到了用户信息所在的entry的DN,这里就需要用这个DN和第一步中得到的password重新绑定LDAP服务器。

           5. 执行完上一步,验证的主要过程就结束了,如果能成功绑定,那么就说明验证成功,如果不行,则应该返回密码错误的信息。

           这5大步就是基于LDAP的一个 “两次绑定” 验证方法。

            为什么基于LDAP进行验证需要“两次”绑定呢,为什么不能取出password然后和输入进行比较呢,试想一下,如果需要读出密码,服务器上的密码存储要么就不加密,直接可以读出,要么客户就需要知道服务器使用的加密方式,这是不安全,也是不好的,服务器不希望加密方式让客户端知道,客户端也不需要知道这么多。而从实际来看,LDAP服务器对于password属性默认都是不可读的,甚至有的服务器根本就不支持password属性可读,遇到这种情况,也就没有办法取得密码了。

           还有一个问题就是,为什么我们需要第一次绑定?为什么不直接使用DN呢,首先就是关于这个DN,对于一般的客户端程序,其并不知道具体的DN是什么。再者让用户输入DN,给用户带来不便的同时,验证也带来问题,因为如果输入的是个目录树而不是所期望的DN,在进行绑定时有可能会让服务器产生不可预料的错误。

           从上面看来,基于LDAP进行身份验证,最好也是最通用的方法就是 “两次绑定”。所谓的绑定是一个authentication(身份验证)的过程,不要把它想像成“绑定”,既然是认证,就需要一个用户名和密码,openldap中如果出示的用户名和密码错误,服务器会尝试匿名认证,就和匿名ftp一样。当然,在现实配置中可能需要在认证不获得成功就不能做查询操作,这些是在slapd.conf文件中通过设置ACL实现的。认证所用的用户名和密码为目录树中某个节点的两个属性(用户名和密码),一般情况下,程序会默认使用uid和userPassword属性。写程序进行认证的时候只要提供这个节点的两个属性就可以了。

    ldap中常见名词解释:

    CN:common name 通用名 

    对象的属性为CN,例如一个用户的名字为:张三,那么“张三”就是一个CN。

    O:organizationName 组织名

    OU : OrganizationUnit 组织单位

    o和ou都是ldap目录结构的一个属性,建立目录的时候可选新建o,ou 等。在配置我司交换设备ldap的时候具体是配置ou,o还是cn等,要具体看ldap服务器的相应目录是什么属性。

    UID: userid

    对象的属性为uid,例如我司一个员工的名字为:zsq,他的UID为:z02691,ldap查询的时候可以根据cn,也可以根据uid。配置ldap查询的时候需要考虑用何种查询方式。具体设备配置根据何种方式查询由ldap服务器的相关配置来决定。

    DC:Domain Component

    DC类似于dns中的每个元素,例如h3c.com,“.”符号分开的两个单词可以看成两个DC。

    DN:Distinguished Name

    类似于DNS,DN与DNS的区别是:组成DN的每个值都有一个属性类型,例如:

    H3c.com是一个dns,那么用dn表示为:dc=h3c,dc=com 级别越高越靠后。H3c和com的属性都是DC。

    DN可以表示为ldap的某个目录,也可以表示成目录中的某个对象,这个对象可以是用户等。

     

    下面是一个简单的ldap认证流程代码:

    import org.junit.Before;
    import org.junit.Test;
     
    import javax.naming.Context;
    import javax.naming.NamingEnumeration;
    import javax.naming.NamingException;
    import javax.naming.directory.Attribute;
    import javax.naming.directory.SearchControls;
    import javax.naming.directory.SearchResult;
    import javax.naming.ldap.Control;
    import javax.naming.ldap.InitialLdapContext;
    import javax.naming.ldap.LdapContext;
    import javax.xml.bind.PropertyException;
    import java.util.*;
     
     
    public class LdapTest {
     
        private final String PROPERTIES_SCOPE = "ldap";
     
        private final String FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
        private  String url = "ldap://192.168.30.254:389";
        private  String base = "1";
        private  LdapContext ctx = null;
        private  Control[] connCtls = null;
        public static LdapContext ctx = null;
        private final Control[] connCtls = null;
     
        @Test
        public void testUser() {
            connLDAP();
            List<User> list = getUser("users");
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
            System.out.println("ending");
        }
        @Test
        public void test() {
            connLDAP();
        }
        public boolean connLDAP() {
             
            Hashtable<String, String> env = new Hashtable<>();
            env.put(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
            env.put(Context.PROVIDER_URL, url);
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, ldapUserDn);
            env.put(Context.SECURITY_CREDENTIALS, ldapPassword);
     
            try {
                ctx = new InitialLdapContext(env, connCtls);
                System.out.println("认证成功!");
                return Boolean.TRUE;
     
            } catch (javax.naming.AuthenticationException e) {
                System.out.println("认证失败 :");
                e.printStackTrace();
                return Boolean.FALSE;
            } catch (Exception e) {
                System.out.println("认证出错 : ");
                e.printStackTrace();
                return Boolean.FALSE;
            } finally {
                close();
            }
        }
        public void close() {
            if (ctx != null) {
                try {
                    ctx.close();
                    System.out.println("连接关闭");
                } catch (NamingException e) {
                    e.printStackTrace();
                }
            }
        }
    public List<User> getUser(String uname) {
            List<User> users = new ArrayList<>();
            HashMap<String, String> map = new HashMap<>();
            SearchControls searchCtls = new SearchControls();
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            //过滤器,可改变查询条件
            String searchFilter = "(&(cn=" + uname + ")(objectClass=person))";
            String searchBase = "DC=maxcrc,DC=com";
            //对象的每个属性名
            String[] returnedAtts = {"mail", "cn", "userPassword", "sn", "uid"};
            searchCtls.setReturningAttributes(returnedAtts);
            NamingEnumeration<SearchResult> answer;
            Map<String, String> mmap = new HashMap<>();
            try {
                answer = ctx.search(searchBase, searchFilter, searchCtls);
                while (answer.hasMoreElements()) {
                    SearchResult sr = (SearchResult) answer.next();
                    String name = sr.getName();
                    User user = new User();
                    if (name.contains("cn") || name.contains("uid")) {
                        String[] s = name.split(",");
                        String[] cns = s[0].split("=");
                        String cn = cns[cns.length - 1];
                        mmap.put(cn, sr.getNameInNamespace());
                        NamingEnumeration<? extends Attribute> attrs = sr.getAttributes().getAll();
                        while (attrs.hasMore()) {
                            Attribute attr = attrs.next();
                            switch (attr.getID()) {
                                case "cn":
                                    user.setCn(attr.get().toString());
                                    break;
                                case "sn":
                                    user.setSn(attr.get().toString());
                                    break;
                                case "mail":
                                    user.setMail(attr.get().toString());
                                    break;
                                case "uid":
                                    user.setUid(attr.get().toString());
                                    break;
                                case "userPassword":
                                    user.setPassword(attr.get().toString());
                                    break;
                            }
                        }
                        users.add(user);
                    }
                }
            } catch (NamingException e) {
                e.printStackTrace();
            }
            mmap.forEach((k, v) -> System.out.println("key : value = " + k + ":" + v));
            return users;
        }
    }
    public class User {
        private String cn;
        private String sn;
        private String mail;
        private String uid;
        private String password;
     
        public String getCn() {
            return cn;
        }
     
        public void setCn(String cn) {
            this.cn = cn;
        }
     
        public String getSn() {
            return sn;
        }
     
        public void setSn(String sn) {
            this.sn = sn;
        }
     
        public String getMail() {
            return mail;
        }
     
        public void setMail(String mail) {
            this.mail = mail;
        }
     
        public String getUid() {
            return uid;
        }
     
        public void setUid(String uid) {
            this.uid = uid;
        }
     
        public String getPassword() {
            return password;
        }
     
        public void setPassword(String password) {
            this.password = password;
        }
     
        @Override
        public String toString() {
            return "User{" +
                    "cn='" + cn + '\'' +
                    ", sn='" + sn + '\'' +
                    ", mail='" + mail + '\'' +
                    ", uid='" + uid + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }

     

    展开全文
  • 通常在根据LDAP进行身份验证时会采取以下三种方法: 1、利用一个LDAP用户...基于LDAP进行身份验证,最好也是最通用的方法就是 “两次绑定”。这种方法的步骤以及优点可参看我另一篇博客:基于LDAP进行验证-方法
    通常在根据LDAP进行身份验证时会采取以下三种方法:

    1、利用一个LDAP用户的用户名和密码绑定到LDAP服务器。

    2、在LDAP中检索一个用户的条目,然后将提供的密码和检索到的LDAP记录中的密码属性相比较。

    3、“两次绑定”验证方法。

    基于LDAP进行身份验证,最好也是最通用的方法就是 “两次绑定”。这种方法的步骤以及优点可参看我的另一篇博客:基于LDAP进行验证-方法和问题

    当前Shiro只支持了第一种方法,即使用用户名和密码到LDAP服务器中进行绑定来判断合法性。

    我自己写了一个认证类,实现了“两次绑定”验证。同时解决了目前做LDAP认证时没有区分错误情况,返回的错误提示信息不够准确的问题。

    配置信息:

    [main]
    ldapRealm = main.java.name.peter.shiro.realm.ldap.LdapAuthenticator
    ldapRealm.rootDN = dc=example,dc=com
    ldapRealm.contextFactory.url = ldap://localhost:389
    ldapRealm.contextFactory.systemUsername = cn=Manager,dc=example,dc=com
    ldapRealm.contextFactory.systemPassword = secret

    认证类:

    /***
     * 基于Ldap进行身份认证,二次绑定方式.
     * 
     * @author wanghao
     * 
     */
    public class LdapAuthenticator extends JndiLdapRealm {
    
    	private static final Logger log = LoggerFactory
    			.getLogger(LdapAuthenticator.class);
    	private String rootDN;
    
    	public LdapAuthenticator() {
    		super();
    	}
    
    	public String getRootDN() {
    		return rootDN;
    	}
    
    	public void setRootDN(String rootDN) {
    		this.rootDN = rootDN;
    	}
    
    	@Override
    	/***
    	 * 认证
    	 */
    	protected AuthenticationInfo doGetAuthenticationInfo(
    			AuthenticationToken token) throws AuthenticationException {
    		AuthenticationInfo info;
    		try {
    			info = queryForAuthenticationInfo(token, getContextFactory());
    		} catch (AuthenticationNotSupportedException e) {
    			String msg = "Unsupported configured authentication mechanism";
    			throw new UnsupportedAuthenticationMechanismException(msg, e);
    		} catch (javax.naming.AuthenticationException e) {
    			String msg = "LDAP authentication failed.";
    			throw new AuthenticationException(msg, e);
    		} catch (NamingException e) {
    			String msg = "LDAP naming error while attempting to authenticate user.";
    			throw new AuthenticationException(msg, e);
    		} catch (UnknownAccountException e) {
    			String msg = "UnknownAccountException";
    			throw new UnknownAccountException(msg, e);
    		} catch (IncorrectCredentialsException e) {
    			String msg = "IncorrectCredentialsException";
    			throw new IncorrectCredentialsException(msg, e);
    		}
    
    		return info;
    	}
    	
    	@Override
    	protected AuthenticationInfo queryForAuthenticationInfo(
    			AuthenticationToken token, LdapContextFactory ldapContextFactory)
    			throws NamingException {
    
    		Object principal = token.getPrincipal();
    		Object credentials = token.getCredentials();
    
    		LdapContext systemCtx = null;
    		LdapContext ctx = null;
    		try {
    			systemCtx = ldapContextFactory.getSystemLdapContext();
    
    			SearchControls constraints = new SearchControls();
    			constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
    			NamingEnumeration results = systemCtx.search(rootDN, "cn="
    					+ principal, constraints);
    			if (results != null && !results.hasMore()) {
    				throw new UnknownAccountException();
    			} else {
    				while (results.hasMore()) {
    					SearchResult si = (SearchResult) results.next();
    					principal = si.getName() + "," + rootDN;
    				}
    				log.info("DN="+principal);
    				try {
    					ctx = ldapContextFactory.getLdapContext(principal,
    							credentials);
    				} catch (NamingException e) {
    					throw new IncorrectCredentialsException();
    				}
    				return createAuthenticationInfo(token, principal, credentials,
    						ctx);
    			}
    		} finally {
    			LdapUtils.closeContext(systemCtx);
    			LdapUtils.closeContext(ctx);
    		}
    	}
    }



    展开全文
  • RFC1113_Internet电子邮件秘密增强第一部分- 信息加密和身份验证步骤 RFC1131_OSPF规范 RFC1132_802.2分组在IPX网络上传输标准 RFC1134_+PPP协议:关于在点到点链路上进行多协议包传送建议 RFC1142 OSI IS-IS ...
  • RFC中文文档-txt

    2009-09-11 14:56:56
    RFC1113 Internet电子邮件秘密增强第一部分- 信息加密和身份验证步骤 RFC1131 OSPF规范 RFC1132 802.2分组在IPX网络上传输标准 RFC1134 +PPP协议:关于在点到点链路上进行多协议包传送建议 RFC1142 OSI IS-IS ...
  • 中文版RFC,共456

    2009-04-19 22:56:29
    RFC1113 Internet电子邮件秘密增强第一部分- 信息加密和身份验证步骤 RFC1131 OSPF规范 RFC1132 802.2分组在IPX网络上传输标准 RFC1134 +PPP协议:关于在点到点链路上进行多协议包传送建议 RFC1142 OSI IS-IS ...
  • SSO-WebDemo

    2013-08-12 20:25:57
    用户每天平均 16 分钟花在身份验证任务上 - 资料来源: IDS 频繁 IT 用户平均有 21 个密码 - 资料来源: NTA Monitor Password Survey 49% 人写下了其密码,而 67% 人很少改变它们 每 79 秒出现一起身份被窃...
  • 多人聊天+用户在线 21个目标文件 摘要:JAVA源码,媒体网络,山寨QQ,Java聊天程序 Java编写山寨QQ,多人聊天+用户在线,程序分服务端和客户端,典型C/S结构, 当用户发送第一次请求时候,验证用户登录,创建一个该...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
    产生RSA密钥对(myKeyPair),得到RSA密钥对,产生Signature对象,对用私钥对信息(info)签名,用指定算法产生签名对象,用私钥初始化签名对象,将待签名数据传送给签名对象(须在初始化之后),用公钥验证签名结果,...
  • Weblogic管理指南 (Doc&Pdf)

    热门讨论 2008-06-18 11:37:13
    将请求重定向到另一个HTTP服务器 1 设置从服务器代理 1 代理...身份验证 1 限制对Web应用资源访问 1 在servlet中使用用户和角色 1 配置WEB应用外部资源 1 在WEB应用中引用EJBS 1 配置...
  • LDAP相对标识名是LDAP标识名一部分,它用来标识容器中对象,它组成随客户建立现场搜索内容大小而变化。搜索内容范围可以是域组件,也可以是普通名字对象。 1 N6 f7 Q+ p: _0 L: e0 n2 n5 o7 g1 y 下表...

空空如也

空空如也

1 2
收藏数 25
精华内容 10
关键字:

ldap身份验证的通用步骤