1.活动目录(AD)

Active Directory 是用于 Windows Server 的目录服务。
它存储着网络上各种对象的有关信息,并使该信息易于管理员和用户查找及使用。
Active Directory 目录服务使用结构化的数据存储作为目录信息的逻辑层次结构的基础。 
通过登录验证以及目录中对象的访问控制,将安全性集成到 Active Directory 中。

目录服务,如 Active Directory,提供了用于存储目录数据并使该数据可由网络用户和管理员使用的方法。
例如,Active Directory 存储了有关用户帐户的信息,如名称、密码、电话号码等,并允许相同网络上的其他已授权用户访问该信息。

2.LDAP

LDAP是轻量目录访问协议,英文全称是Lightweight Directory Access Protocol。
LDAP是基于X.500标准的。
LDAP 仅通过使用原始 X.500目录存取协议 (DAP) 的功能子集而减少了所需的系统资源消耗。
与X.500不同,LDAP支持TCP/IP,这对访问Internet是必须的。
LDAP和关系数据库是两种不同层次的概念,后者是存贮方式(同一层次如网格数据库,对象数据库),前者是存贮模式和访问协议。
LDAP是一个比关系数据库抽象层次更高的存贮概念,与关系数据库的查询语言SQL属同一级别。

3.ADSI

在Delphi中可以使用微软的ADSI(活动目录服务接口)来访问活动目录。
ADSI是一组以COM接口的形式提供目录服务的,是为基于目录服务提供的通用接口。
一些标准的ADSI提供者(Provider)有WinNT、IIS、LDAP和NDS。
可以通过ADSI存取四种网络目录结构:
WinNT (Microsoft SAM 数据库)、LDAP (轻量目录存取协议)、NDS (NetWare目录服务)和NWCOMPAT(Novell NetWare 3.x)。 
ADSI可以使Windows NT 管理员的工作变得轻松。
ADSI支持管理员执行一些一般的管理任务,比如添加新用户、管理打印机、安全设定和控制NT域。
因为ADSI使用COM接口,任何支持COM的编程语言像Delphi、BCB、VB、VC等都可以调用ADSI。

如在Delphi中调用ADSI,则需要引入活动目录类型库。
操作如下:
在IDE中,Project--->Import Type Library。
选择“Active Ds Type Library(Version 1.0)”,单击“Create Unit”。
Delphi会做相应的封装,生成ActiveDs_TLB.pas文件。
Uses ActiveDs_TLB,就可以在Delphi程序中使用ADSI了。

4.JAVA+LDAP访问Window 2000 Server AD。

package ADOper;

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.NamingEnumeration;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import java.util.Enumeration;

public class ADOperTest {
public ADOperTest() {
}

public void GetADInfo() {
    Hashtable HashEnv = new Hashtable();

String LDAP_URL = "ldap://192.168.100.3:389"; //LDAP访问地址
    //String adminName = "CN=OAWebUser,CN=Users,DC=Hebmc,DC=com";//AD的用户名
    String adminName = "Hebmc\\OAWebUser"; //注意用户名的写法:domain\User 或 User@domain.com
    adminName = "OAWebUser@Hebmc.com"; //注意用户名的写法:domain\User 或 User@domain.com
    String adminPassword = "chenzuooaup02"; //密码

HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple"); //LDAP访问安全级别
    HashEnv.put(Context.SECURITY_PRINCIPAL, adminName); //AD User
    HashEnv.put(Context.SECURITY_CREDENTIALS, adminPassword); //AD Password
    HashEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); //LDAP工厂类
    HashEnv.put(Context.PROVIDER_URL, LDAP_URL);

try {
      LdapContext ctx = new InitialLdapContext(HashEnv, null);
      SearchControls searchCtls = new SearchControls(); //Create the search controls
      searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); //Specify the search scope

String searchFilter = "objectClass=User"; //specify the LDAP search filter
      //String searchFilter = "objectClass=organizationalUnit";//specify the LDAP search filter

String searchBase = "DC=Hebmc,DC=com"; //Specify the Base for the search//搜索域节点
      int totalResults = 0;

//Specify the attributes to return
      //String returnedAtts[] = {"memberOf"};//定制返回属性
      String returnedAtts[] = {
          "url", "whenChanged", "employeeID", "name", "userPrincipalName",
          "physicalDeliveryOfficeName", "departmentNumber", "telephoneNumber",
          "homePhone", "mobile", "department", "sAMAccountName", "whenChanged",
          "mail"}; //定制返回属性

searchCtls.setReturningAttributes(returnedAtts); //设置返回属性集

//Search for objects using the filter
      NamingEnumeration answer = ctx.search(searchBase, searchFilter,searchCtls);

while (answer.hasMoreElements()) {
        SearchResult sr = (SearchResult) answer.next();
        System.out.println("************************************************");
        System.out.println(sr.getName());

Attributes Attrs = sr.getAttributes();
        if (Attrs != null) {
          try {
            for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore(); ) {
              Attribute Attr = (Attribute) ne.next();

System.out.println(" AttributeID=" + Attr.getID().toString());

//读取属性值
              for (NamingEnumeration e = Attr.getAll(); e.hasMore();totalResults++) {
                System.out.println("    AttributeValues=" + e.next().toString());
              }
              System.out.println("    ---------------");

//读取属性值
              Enumeration values = Attr.getAll();
              if (values != null) { // 迭代
                while (values.hasMoreElements()) {
                  System.out.println("    AttributeValues=" + values.nextElement());
                }
              }
              System.out.println("    ---------------");
            }
          }
          catch (NamingException e) {
            System.err.println("Throw Exception : " + e);
          }
        }
      }
      System.out.println("Number: " + totalResults);
      ctx.close();
    }

catch (NamingException e) {
      e.printStackTrace();
      System.err.println("Throw Exception : " + e);
    }
}

public static void main(String args[]) {
    ADOperTest ad = new ADOperTest();
    ad.GetADInfo();
}
}

备注:

使用LADP访问AD,注意用户名的写法:domain\User 或 User@domain.com

如用户名不正确,则可能会出现如下异常:

javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 525, vece

5.Delphi7使用WinNT Provider访问Window 2000 Server AD。

unit Unt_AD;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ActiveDs_TLB, ActiveX, ComObj;

type
TMainFrm = class(TForm)
    grp1: TGroupBox;
    cbUseLogin: TCheckBox;
    lbl1: TLabel;
    ADSIUsername: TEdit;
    lbl2: TLabel;
    ADSIPassword: TEdit;
    ADSIDomainName: TEdit;
    btn1: TButton;
    lbl3: TLabel;
    GroupListView: TListView;
    ComputerListView: TListView;
    SeverListView: TListView;
    UserListView: TListView;
    lbl4: TLabel;
    lbl5: TLabel;
    Label1: TLabel;
    lbl6: TLabel;
    procedure btn1Click(Sender: TObject);
private
    { Private declarations }
    procedure GetDomainInformation(Domain: IADsContainer);
    procedure AddUserToList(ADsObj:IADs);
    procedure AddGroupToList(ADsObj:IADs);
    procedure AddComputerToList(ADsObj:IADs);
public
    { Public declarations }
end;

//连接 Win NT 目录服务就是找到域控制器然后绑定到相应的对象上。
//绑定可以通过 ADsGetObject 或 ADsOpenObject 函数来实现。
//第一个函数使用登录用户缺省的信任级别,
//第二个函数允许开发者指定特殊的安全信任机制来绑定 ADSI 对象。

//缺省条件下,ADsGetObject函数根据当前用户进行安全认证。
function ADsGetObject(lpszPathName: PWideChar;   //第一个参数是对象的路径名
                      const riid: TIID;          //第二个参数是对象的接口标识符
                      out obj): HResult; stdcall; external 'activeds.dll';// 第三个参数用于返回得到的被请求的接口指针

//ADsOpenObject 函数在不同的安全认证机制下绑定 ADSI 对象,
//它主要是通过调用参数返回的用户名和口令来认证的
function ADsOpenObject(lpszPathName: PWideChar;    //第一个参数是对象的路径名
                       lpszUserName: PWideChar;    //第二个参数是调用者提供的用户名
                       lpszPassword: PWideChar;    //第三个参数是调用者提供的口令
                       dwReserved: LongInt;        //第四个参数是一个保留的 provider 标识,用来确定绑定的认证方法
                       const riid: TIID;           //第五个参数是请求接口的接口标识符,
                       out obj): HResult; stdcall; external 'activeds.dll'; //最后一个参数用来返回请求的接口指针。

var
MainFrm: TMainFrm;

implementation

{$R *.dfm}

// 获取域信息 
procedure TMainFrm.GetDomainInformation(Domain: IADsContainer);
var
Enum: IEnumVariant;
ADsTempObj: OLEVariant;
ADsObj: IADs;
Value: LongWord;
begin
//清空用户、组和计算机列表
UserListView.Items.Clear;
GroupListView.Items.Clear;
ComputerListView.Items.Clear;

//获取枚举对象,并赋值给 Enum 变量
Enum := (Domain._NewEnum) as IEnumVariant;

//利用枚举对象查找,把每个子对象赋值给临时的 OLEVariant 对象
while (Enum.Next(1, ADsTempObj, Value) = S_OK) do
    begin
      ADsObj := IUnknown(ADsTempObj) as IADs; //获得临时对象:OLEVariant 变量赋值给 ADSI 对象

if AdsObj.Class_ = 'User' then    //如果是用户对象
          AddUserToList(ADsObj);

if AdsObj.Class_ = 'Group' then   //如果是组对象
         AddGroupToList(ADsObj);

if AdsObj.Class_ = 'Computer' then //如果是计算机对象
         AddComputerToList(ADsObj);

ADsTempObj:=Null; //释放OLEVariant
end; 
end;

procedure TMainFrm.AddUserToList(ADsObj:IADs);
var aListItem:TListItem;
begin
aListItem:=UserListView.Items.Add;
aListItem.Caption:=ADsObj.Name;
aListItem.SubItems.Add(ADsObj.Class_);
aListItem.SubItems.Add(ADsObj.ADsPath);
aListItem.SubItems.Add(ADsObj.Parent);
// aListItem.SubItems.Add(ADsObj.Get('sAMAccountName'));
end;

procedure TMainFrm.AddGroupToList(ADsObj:IADs);
var aListItem:TListItem;
begin
aListItem:=GroupListView.Items.Add;
aListItem.Caption:=ADsObj.Name;
aListItem.SubItems.Add(ADsObj.Class_);
aListItem.SubItems.Add(ADsObj.ADsPath);
end;

procedure TMainFrm.AddComputerToList(ADsObj:IADs);
var aListItem:TListItem;
begin
aListItem:=ComputerListView.Items.Add;
aListItem.Caption:=ADsObj.Name;
aListItem.SubItems.Add(ADsObj.Class_);
aListItem.SubItems.Add(ADsObj.ADsPath);
end;

procedure TMainFrm.btn1Click(Sender: TObject);
var
    UnknownObject: IUnknown;
    DomainPath,ADUser,ADPass: WideString;
    Domain: IADsContainer;       
begin
// 指定域路径
DomainPath := 'WinNT://' + ADSIDomainName.Text;
ADUser:= ADSIUsername.Text;
ADPass:= ADSIPassword.Text;

DomainPath := 'WinNT://Hebmc.com';
ADUser: ADUser:= 'Hebmc\OAWebUser'; //注意用户名称的写法:domain\User 或 User@domain.com
ADUser:= 'OAWebUser@Hebmc.com'; //注意用户名称的写法:domain\User 或 User@domain.com
ADPass:= 'chenzuooaup02';

// 如果使用用户登录了信息
if cbUseLogin.Checked then // 使用用户登录的信息创建域对象
     OleCheck(AdsOpenObject(PWideChar(DomainPath),
              PWideChar(ADUser),
              PWideChar(ADPass),
              0,
              IID_IADsContainer,
              UnknownObject))
else
     OleCheck(ADsGetObject(PWideChar(DomainPath),
              IID_IADsContainer,
              UnknownObject));

// 设定域对象
Domain := UnknownObject as IADsContainer;

// 从域中获得信息列表
GetDomainInformation(Domain); 
end;

end.

备注:

*.注意用户名的写法:domain\User 或 User@domain.com

*.Delphi使用WinNT Provider方式访问AD,在使用ADsObj.Get('属性')时,会有一个报错:“高速缓存中找不到目录属性。” (The directory property cannot be found in the cache)。
    目前该问题还不知如何解决。

*.资料参考《Delphi深度探索-活动目录开发》。
    作者:陈省(Hubdog)。 http://hubdog.csdn.net/

6.Delphi7使用LDAP访问Window 2000 Server AD。

unit Main;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ActiveDs_TLB, ActiveX, ComObj;

type
TMainFrm = class(TForm)
    Button1: TButton;
    ObjMemo: TMemo;
    OUMemo: TMemo;
    UserMemo: TMemo;
    cbUseLogin: TCheckBox;
    Obj_Label: TLabel;
    OU_Label: TLabel;
    Users_Label: TLabel;
    procedure Button1Click(Sender: TObject);
private
    { Private declarations }
public
    { Public declarations }
    procedure GetADInfo(aUser,aPass,aDomainPath: WideString);
end;

//连接 Win NT 目录服务就是找到域控制器然后绑定到相应的对象上。
//绑定可以通过 ADsGetObject 或 ADsOpenObject 函数来实现。
//第一个函数使用登录用户缺省的信任级别,
//第二个函数允许开发者指定特殊的安全信任机制来绑定 ADSI 对象。

//缺省条件下,ADsGetObject函数根据当前用户进行安全认证。
function ADsGetObject(lpszPathName: PWideChar;   //第一个参数是对象的路径名
                      const riid: TIID;          //第二个参数是对象的接口标识符
                      out obj): HResult; stdcall; external 'activeds.dll';// 第三个参数用于返回得到的被请求的接口指针

//ADsOpenObject 函数在不同的安全认证机制下绑定 ADSI 对象,
//它主要是通过调用参数返回的用户名和口令来认证的
function ADsOpenObject(lpszPathName: PWideChar;    //第一个参数是对象的路径名
                       lpszUserName: PWideChar;    //第二个参数是调用者提供的用户名
                       lpszPassword: PWideChar;    //第三个参数是调用者提供的口令
                       dwReserved: LongInt;        //第四个参数是一个保留的 provider 标识,用来确定绑定的认证方法
                       const riid: TIID;           //第五个参数是请求接口的接口标识符,
                       out obj): HResult; stdcall; external 'activeds.dll'; //最后一个参数用来返回请求的接口指针。

var
MainFrm: TMainFrm;

implementation

{$R *.dfm}

procedure TMainFrm.Button1Click(Sender: TObject);
var DomainPath,ADUser,ADPass: WideString;
begin
DomainPath := 'LDAP://192.168.100.3/OU=省公司,OU=组织架构,DC=Hebmc,DC=com';//LDAP访问AD的路径。

ADUser:='Hebmc\OAWebUser'; //注意用户名称的写法:域名称 + 用户名称 或 User@domain.com
ADUser:='OAWebUser@Hebmc.com'; //注意用户名称的写法:域名称 + 用户名称 或 User@domain.com
ADPass:='chenzuooaup02';   //用户密码。

GetADInfo(ADUser,ADPass,DomainPath); 
end;

procedure TMainFrm.GetADInfo(aUser,aPass,aDomainPath: WideString);
var
UnknownObject: IUnknown;
Enum: IEnumVariant;
ADsTempObj: OLEVariant;
Domain: IADsContainer;
ADsObj: IADs;
Value: LongWord;
begin

// 如果使用AD用户信息登录
if cbUseLogin.Checked then // 使用用户登录的信息创建域对象
     OleCheck(AdsOpenObject(PWideChar(aDomainPath),
              PWideChar(aUser),
              PWideChar(aPass),
              0,
              IID_IADsContainer,
              UnknownObject))
else 
     OleCheck(ADsGetObject(PWideChar(aDomainPath),
              IID_IADsContainer,
              UnknownObject));

// 设定域对象
Domain := UnknownObject as IADsContainer;

//获取枚举对象,并赋值给 Enum 变量
Enum := (Domain._NewEnum) as IEnumVariant;

//利用枚举对象查找,把每个子对象赋值给临时的 OLEVariant 对象
while (Enum.Next(1, ADsTempObj, Value) = S_OK) do
    begin
      ADsObj := IUnknown(ADsTempObj) as IADs; //获得临时对象:OLEVariant 变量赋值给 ADSI 对象

ObjMemo.Lines.Add(AdsObj.Class_); //对象类型

if AdsObj.Class_ = 'organizationalUnit' then //如果是组织单元对象
         OUMemo.Lines.Add(ADsObj.Name) ;

if AdsObj.Class_ = 'user' then //如果是用户对象
         UserMemo.Lines.Add(ADsObj.Name+'(用户代码='+ADsObj.Get('sAMAccountName')+')');

//递归。得到组内相关用户。
      GetADInfo(aUser,aPass,ADsObj.ADsPath);

ADsTempObj:=Null; //释放OLEVariant
end;

end;

end.

备注:

*. 注意用户名的写法:domain\User 或 User@domain.com

*. Delphi使用LDAP方式访问AD,可以使用ADsObj.Get('属性')时得到相关属性值。
    属性的取值域可以通过JAVA程序得到。


Directory Services(目录服务)

我们知道,当局域网的规模变的越来越大时,为了方便主机管理,我们使用DHCP来实现IP地址、以太网地址、主机名和拓扑结构等的集中管理和统一分配。同样,如果一个局域网内有许多的其它资源时,如打印机、共享文件夹等等,为了方便的定位及查找它们,一种集中定位管理的方式或许是较好的选择,DNS和NIS都是用来实现类似管理的方法。

对于局域网内的一个用户来讲,工作等其它应用需要,我们必须凭帐号登录主机、用帐号收发E-mail,甚至为了管理需要公司还需要维护一个电子号码簿来存储员工的姓名、地址、电话号码等信息。随着时间的增长,我们会为这些越来越多的帐号和密码弄的头晕脑胀。同时,如果一个员工离开,管理员就不得不翻遍所有的记录帐号信息的文件把离职员工的信息删除。这些将是一个繁琐而效率低下的工作。那么,如果能将此些帐号信息等统一到一个文件中进行管理,无疑会大大提高员工及管理员的工作效率。目录服务(LDAP是其实现的一种)正是基于这些应用实现的。

LDAP

LDAP是Lightweight Directory Access Protocol的缩写,顾名思义,它是指轻量级目录访问协议(这个主要是相对另一目录访问协议X.500而言的;LDAP略去了x.500中许多不太常用的功能,且以TCP/IP协议为基础)。目录服务和数据库很类似,但又有着很大的不同之处。数据库设计为方便读写,但目录服务专门进行了读优化的设计,因此不太适合于经常有写操作的数据存储。同时,LDAP只是一个协议,它没有涉及到如何存储这些信息,因此还需要一个后端数据库组件来实现。这些后端可以 是bdb(BerkeleyDB)、ldbm、shell和passwd等。

LDAP目录以树状的层次结构来存储数据(这很类同于DNS),最顶层即根部称作“基准DN”,形如"dc=mydomain,dc=org"或者"o= mydomain.org",前一种方式更为灵活也是Windows AD中使用的方式。在根目录的下面有很多的文件和目录,为了把这些大量的数据从逻辑上分开,LDAP像其它的目录服务协议一样使用OU (Organization Unit),可以用来表示公司内部机构,如部门等,也可以用来表示设备、人员等。同时OU还可以有子OU,用来表示更为细致的分类。

LDAP中每一条记录都有一个唯一的区别于其它记录的名字DN(Distinguished Name),其处在“叶子”位置的部分称作RDN;如dn:cn=tom,ou=animals,dc=mydomain,dc=org中tom即为 RDN;RDN在一个OU中必须是唯一的。

因为LDAP数据是“树”状的,而且这棵树是可以无限延伸的,假设你要树上的一个苹果(一条记录),你怎么告诉园丁它的位置呢?当然首先要说明是哪一棵树(dc,相当于MYSQL的DB),然后是从树根到那个苹果所经过的所有“分叉”(ou,呵呵MYSQL里面好象没有这 DD),最后就是这个苹果的名字(uid,记得我们设计MYSQL或其它数据库表时,通常为了方便管理而加上一个‘id’字段吗?)。好了!这时我们可以清晰的指明这个苹果的位置了,就是那棵“歪脖树”的东边那个分叉上的靠西边那个分叉的再靠北边的分叉上的半红半绿的……,晕了!你直接爬上去吧!我还是说说LDAP里要怎么定义一个字段的位置吧,树(dc=waibo,dc=com),分叉(ou=bei,ou=xi,ou= dong),苹果(cn=honglv),好了!位置出来了: 
  dn:cn=honglv,ou=bei,ou=xi,ou=dong,dc=waibo,dc=com 
LDAP的 可扩展性和灵活性

LDAP 协议既是跨平台的也是基于标准的。这意味着几乎在任何计算机平台上运行的任何应用程序都可以从LDAP目录获取信息。另外,无论什么服务器操作系统、文件系统或平台对于客户机都是无关紧要的。

LDAP目录几乎可以存储所有类型的数据:电子邮件地址、DNS 信息、NIS 映射、安全性密钥、联系人信息列表和计算机名等。如果需要专门的组织单元或项,则可以根据具体实现来定制控制给定字段可以保存哪种信息的规则(称为模式,稍后将详细讨论)。

大多数 LDAP 服务器的安装和配置相对比较简单,并且可以在很少或没有维护的情况下运行多年,而且很容易为特定类型的访问而进行最优化。

可以容易地配置 LDAP 目录来复制部分或所有目录树(使用推(push)或拉(pull)方法)。这可以使系统管理员不必担心出现单点故障的情况。

可以通过 ACL(访问控制表,Access Control List)来控制对目录的访问。例如,管理员可以根据给定组或位置中的成员资格来限制谁可以看到哪些内容,或者给予特殊用户在其自己记录中修改所选字段的能力。ACL 提供极其细粒度的访问控制,而且 ACL 将这种控制与 LDAP 安装结合在一起,而不是与请求信息的客户机结合在一起。此外,可以容易地将 LDAP 与大多数现有的安全性层和/或认证系统(例如 SSL、Kerberos 和 PAM 等)集成在一起。

LDIF

LDIF(LDAP Interchange Format)是指存储LDAP配置信息及目录内容的标准文本文件格式,之所以使用文本文件来格式来存储这些信息是为了方便读取和修改,这也是其它大多数 服务配置文件所采取的格式。LDIF文件常用来向目录导入或更改记录信息,这些信息需要按照LDAP中schema的格式进行组织,并会接受schema 的检查,如果不符合其要求的格式将会出现报错信息。LDIF文件样例如下:

dn:cn=stan,ou=linux,ou=computer,dc=ourschool,dc=org

objectClass:organizationalPerson

cn:stan

cn:czm

sn:czm

其中,以“#”号开头的为注释行;第二行起的行中,冒号左边为属性,右边是属性的值,这类同于编程中的变量及为其所赋的值,但属性可以被重复赋值。
注意:同一个属性可以有一个或者多个值,ldap在寻址时,可以根据同一个属性的不同值进行寻址,例如上例中可以根据cn属性的stan和czm进行寻址,这样速度更快。

objectClass 

对象类由 LDAP 目录使用来定义给定类型的对象可以有哪些属性。对象类还定义项必须有什么属性,以及项可以有什么属性。所有对象类都从其父对象类继承需求,然后添加它们自己的需求。

对象类有五个组件:OID(对象标识)、唯一名称、父对象(SUP)、任何需要的属性(MUST)和允许的属性列表(MAY)。OID是由LDAP目录的内部数据库机制使用的数据标识符。从概念上讲,它们与IP地址相似,因为每个对象类都必须有一个唯一数字。并且象DNS和IP之间的关系那样,由创建它们的个人进行注册,并由这些人“拥有”。

在LDAP中objectClass分为三种:Abstract,Structural,AUXIALIARY。要定义一个Entry必须包含一个Structural类型的ObjectClass,

其他两个类型可包括0或多个。其中Top是一个顶级ObjectClass,里面定义了一个MUST Attribute:ObjectClass,于是也就决定了必须有

一个其它的Structural ObjectClass才能定义一个Entry.其中ObjectClass又可以存在继承关系,子ObjectClass会继承父ObjectClass中的

全部Attribute.该继承关系于Java中有点相似.

在LDAP中每一个ObjectClass都定义了一些Attribute,其Attribute仍然可以是ObjectClass。在这些Attriubte中分为两种类型MUST,MAY, MUST表示这个Entry必须包括的属性,MAY为可选。一个ObjectClass的Attribute也包括所有继承自父ObjectClass和自身定义的ObjectClass。

下面用一个类型进行说明:

objectclass ( 2.5.6.0 NAME 'top' ABSTRACT

MUST objectClass )

objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'

DESC 'RFC2247: domain component object'

SUP top AUXILIARY

MUST dc )

上面是两个objectclass的定义,其中top为ABSTRACT,dcObject为AUXILIARY,这两个类型都不能定义Entry.

下面这个LDIF文件在导入到LDAP时会出错:

dn: dc=java,dc=com

objectClass:dcObject

dc: java.com

要定义这个Entry必须找到一个STRUCTURAL类型的ObjectClass。

objectClasses: ( 2.5.6.4 NAME 'organization'

DESC 'RFC2256: an organization' SUP top STRUCTURAL

MUST o

MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory

$ x121Address $ registeredAddress $ destinationIndicator

$ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier

$ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber

$ street $ postOfficeBox $ postalCode $ postalAddress

$ physicalDeliveryOfficeName $ st $ l $ description ) )

这个objectClass的类型为STRUCTURAL,因此可以用来定义Entry.具体定义如下

dn: dc=java,dc=com

objectClass:dcObject

objectClass:organization

dc: java.com

o: java.com

Attribute

Attribute类同于编程语言中的变量,它可以被赋值,就像是可以存放一个单一类型信息的容器。官方声明了许多常用的 Attribute,如果其中没有你所需要的,你可以自己定义,但要避免重名。objectClass是一种特殊的Attribute,它包含其它用到的 Attribute以及它自身。常见的Attribute如下:

schema

LDAP中,schema用来指定一个目录中所包含的objects的类型 (objectClass)以及每一个objectClass中的各个必备(mandatory)和可选(optional)的属性 (attribute)。因此,Schema是一个数据模型,它被用来决定数据怎样被存储,被跟踪的数据的是什么类型,存储在不同的Entry下的数据之间的关系。schema 需要在主配置文件slapd.conf中指定,以用来决定本目录中使用到的objectClass。管理员可以自己设计制定schema,一般包括属性定 义(AttributeDefinition)、类定义(ClassDefinition)以及语法定义(SyntaxDefinition)等部分。

LDAP V3中在x.500标准的基础上定义了一个包含了网络中大多常见对象的schema,这些对象包括国家、所在地、组织、人员、小组以及设备等。同时,LDAP V3中可以很方便的从目录中提取出schema,它正是一条记录中关于属性的声明部分。

对象标识符

对象标识符( Object Identifiers )是被LDAP内部数据库引用的数字标识。Attribute的名字是设计为方便人们读取的,但为了方便计算机的处理,通常使用一组数字来标识这些对象,这类同于SNMP中的MIB2。例如,当计算机接收到dc这个Attribute时,它会将这个名字转换为对应的OID: 1.3.6.1.4.1.1466.115.121.1.26。

几点体会

重大误解

很多所谓基于LDAP实现的认证都是使用目录来存储加密(多数是MD5)后的密码而已.但是这种密码很好被暴力破解.于是你的系统的安全状态有退回到未 shadow过的/etc/passwd时代.在介绍目录服务的书籍中提到/etc/passwd也算是一种目录.我们不能说一个文本文件是用来做认证的吧?

很多LDAP的实现和应用都不使用LDAP来作验证.如微软的活动目录.还有斯坦福大学的目录都使用了kerberos.配置好的Windows域控制器本身就是一个Kerberos KDC.而斯坦福的目录也采用了Hedimal的Kerberos实现来做KDC和一个Windows 的KDC.这样的理由很明显的.如果要通过过LDAP目录来实现一个十分安全的认证信息的存储要借助大量的外围协议.还不如直接使用这些协议本身.长话短说,我们应该让目录存储它改存储的东西.

产品选择

产品的选择取决于你部署目录服务的目的以及内容.如果活动目录提供给你的那些的功能足够你的需要.那么采用活动目录好了.因为他一开始就将安全实现的很好(操作系统安全不在讨论之列).而且配置简单.而且和客户端(windows)兼容的较好.

如果你想在深入研究LDAP协议.或者要实现一个高度自定义的目录.那么选择OpenLDAP吧!

安全措施.

尽量少将安全敏感的信息存储在目录中.如果非存不可.一定要编辑ACL严格的控制访问权限如userPassword等.多数情况下目录服务还要主要是给其他的服务提供数据存储.这样的话应该让每个服务器绑定到不通的DN,同时在ACL中赋予他们较高的权限.而不能让所有的服务都使用rootdn来绑定.

目录结构的设计

虽然目录结构的设计可以比较灵活.但比较有经验的设计可以使你的目录结构更加合理,而且可以给你带来意想不到的好处.尽量参照前人实现的产品一吸收好的思想.分析微软的设计,在目录中建立分组的目录树是明智的做法.这样每个帐户的memberof属性的值他所属的组的DN,这样就可以方便的将基于组的访问控制列表也实现在目录结构中.相象如果活动目录中用户的memberof属性是"Aministrators"而不是"CN= Administrators,CN=Builtin,DC=example,DC=edu,DC=cn"的话会给微软带来多少麻烦?尽量的将更多的数据以树的形式组织,同时在字段中使用更多的DN代替单一的数值,可以最大限度的发挥目录服务的潜力.
AD和LDAP的关系

Active  Directory应该是LDAP的一个应用实例,而不应该是LDAP本身。比如:windows域控的用户、权限管理应该是微软公司使用LDAP存储了一些数据来解决域控这个具体问题,
只是AD顺便还提供了用户接口,也可以利用Active Directory当做LDAP服务器存放一些自己的东西而已。比如LDAP是关系型数据库,微软自己在库中建立了几个表,每个表都定义好了字段。显然这些表和字段都是根据微软自己的需求定制的,而不是LDAP协议的规定。然后微软将LDAP做了一些封装接口,用户可以利用这些接口写程序操作LDAP,使得Active Directory也成了一个LDAP服务器。
总之:Active Directory = LDAP服务器+LDAP应用(Windows域控)。Active Directory先实现一个LDAP服务器,然后自己先用这个LDAP服务器实现了自己的一个具体应用(域控)

参考至:http://doc.linuxpk.com/2293.html
             http://doc.linuxpk.com/2583.html 
             http://doc.linuxpk.com/2586.html 
             http://doc.linuxpk.com/28400.html
             http://bbs.chinaunix.net/thread-889055-1-1.html 
             http://hi.baidu.com/dongyuejiang/item/d2af278125f05d5d26ebd9a0
             http://linux.chinaitlab.com/special/ldap/Index.html

Directory Services(目录服务)

我们知道,当局域网的规模变的越来越大时,为了方便主机管理,我们使用DHCP来实现IP地址、以太网地址、主机名和拓扑结构等的集中管理和统一分配。同样,如果一个局域网内有许多的其它资源时,如打印机、共享文件夹等等,为了方便的定位及查找它们,一种集中定位管理的方式或许是较好的选择,DNS和NIS都是用来实现类似管理的方法。

对于局域网内的一个用户来讲,工作等其它应用需要,我们必须凭帐号登录主机、用帐号收发E-mail,甚至为了管理需要公司还需要维护一个电子号码簿来存储员工的姓名、地址、电话号码等信息。随着时间的增长,我们会为这些越来越多的帐号和密码弄的头晕脑胀。同时,如果一个员工离开,管理员就不得不翻遍所有的记录帐号信息的文件把离职员工的信息删除。这些将是一个繁琐而效率低下的工作。那么,如果能将此些帐号信息等统一到一个文件中进行管理,无疑会大大提高员工及管理员的工作效率。目录服务(LDAP是其实现的一种)正是基于这些应用实现的。

LDAP

LDAP是Lightweight Directory Access Protocol的缩写,顾名思义,它是指轻量级目录访问协议(这个主要是相对另一目录访问协议X.500而言的;LDAP略去了x.500中许多不太常用的功能,且以TCP/IP协议为基础)。目录服务和数据库很类似,但又有着很大的不同之处。数据库设计为方便读写,但目录服务专门进行了读优化的设计,因此不太适合于经常有写操作的数据存储。同时,LDAP只是一个协议,它没有涉及到如何存储这些信息,因此还需要一个后端数据库组件来实现。这些后端可以 是bdb(BerkeleyDB)、ldbm、shell和passwd等。

LDAP目录以树状的层次结构来存储数据(这很类同于DNS),最顶层即根部称作“基准DN”,形如"dc=mydomain,dc=org"或者"o= mydomain.org",前一种方式更为灵活也是Windows AD中使用的方式。在根目录的下面有很多的文件和目录,为了把这些大量的数据从逻辑上分开,LDAP像其它的目录服务协议一样使用OU (Organization Unit),可以用来表示公司内部机构,如部门等,也可以用来表示设备、人员等。同时OU还可以有子OU,用来表示更为细致的分类。

LDAP中每一条记录都有一个唯一的区别于其它记录的名字DN(Distinguished Name),其处在“叶子”位置的部分称作RDN;如dn:cn=tom,ou=animals,dc=mydomain,dc=org中tom即为 RDN;RDN在一个OU中必须是唯一的。

因为LDAP数据是“树”状的,而且这棵树是可以无限延伸的,假设你要树上的一个苹果(一条记录),你怎么告诉园丁它的位置呢?当然首先要说明是哪一棵树(dc,相当于MYSQL的DB),然后是从树根到那个苹果所经过的所有“分叉”(ou,呵呵MYSQL里面好象没有这 DD),最后就是这个苹果的名字(uid,记得我们设计MYSQL或其它数据库表时,通常为了方便管理而加上一个‘id’字段吗?)。好了!这时我们可以清晰的指明这个苹果的位置了,就是那棵“歪脖树”的东边那个分叉上的靠西边那个分叉的再靠北边的分叉上的半红半绿的……,晕了!你直接爬上去吧!我还是说说LDAP里要怎么定义一个字段的位置吧,树(dc=waibo,dc=com),分叉(ou=bei,ou=xi,ou= dong),苹果(cn=honglv),好了!位置出来了: 
  dn:cn=honglv,ou=bei,ou=xi,ou=dong,dc=waibo,dc=com 
LDAP的 可扩展性和灵活性

LDAP 协议既是跨平台的也是基于标准的。这意味着几乎在任何计算机平台上运行的任何应用程序都可以从LDAP目录获取信息。另外,无论什么服务器操作系统、文件系统或平台对于客户机都是无关紧要的。

LDAP目录几乎可以存储所有类型的数据:电子邮件地址、DNS 信息、NIS 映射、安全性密钥、联系人信息列表和计算机名等。如果需要专门的组织单元或项,则可以根据具体实现来定制控制给定字段可以保存哪种信息的规则(称为模式,稍后将详细讨论)。

大多数 LDAP 服务器的安装和配置相对比较简单,并且可以在很少或没有维护的情况下运行多年,而且很容易为特定类型的访问而进行最优化。

可以容易地配置 LDAP 目录来复制部分或所有目录树(使用推(push)或拉(pull)方法)。这可以使系统管理员不必担心出现单点故障的情况。

可以通过 ACL(访问控制表,Access Control List)来控制对目录的访问。例如,管理员可以根据给定组或位置中的成员资格来限制谁可以看到哪些内容,或者给予特殊用户在其自己记录中修改所选字段的能力。ACL 提供极其细粒度的访问控制,而且 ACL 将这种控制与 LDAP 安装结合在一起,而不是与请求信息的客户机结合在一起。此外,可以容易地将 LDAP 与大多数现有的安全性层和/或认证系统(例如 SSL、Kerberos 和 PAM 等)集成在一起。

LDIF

LDIF(LDAP Interchange Format)是指存储LDAP配置信息及目录内容的标准文本文件格式,之所以使用文本文件来格式来存储这些信息是为了方便读取和修改,这也是其它大多数 服务配置文件所采取的格式。LDIF文件常用来向目录导入或更改记录信息,这些信息需要按照LDAP中schema的格式进行组织,并会接受schema 的检查,如果不符合其要求的格式将会出现报错信息。LDIF文件样例如下:

dn:cn=stan,ou=linux,ou=computer,dc=ourschool,dc=org

objectClass:organizationalPerson

cn:stan

cn:czm

sn:czm

其中,以“#”号开头的为注释行;第二行起的行中,冒号左边为属性,右边是属性的值,这类同于编程中的变量及为其所赋的值,但属性可以被重复赋值。
注意:同一个属性可以有一个或者多个值,ldap在寻址时,可以根据同一个属性的不同值进行寻址,例如上例中可以根据cn属性的stan和czm进行寻址,这样速度更快。

objectClass 

对象类由 LDAP 目录使用来定义给定类型的对象可以有哪些属性。对象类还定义项必须有什么属性,以及项可以有什么属性。所有对象类都从其父对象类继承需求,然后添加它们自己的需求。

对象类有五个组件:OID(对象标识)、唯一名称、父对象(SUP)、任何需要的属性(MUST)和允许的属性列表(MAY)。OID是由LDAP目录的内部数据库机制使用的数据标识符。从概念上讲,它们与IP地址相似,因为每个对象类都必须有一个唯一数字。并且象DNS和IP之间的关系那样,由创建它们的个人进行注册,并由这些人“拥有”。

在LDAP中objectClass分为三种:Abstract,Structural,AUXIALIARY。要定义一个Entry必须包含一个Structural类型的ObjectClass,

其他两个类型可包括0或多个。其中Top是一个顶级ObjectClass,里面定义了一个MUST Attribute:ObjectClass,于是也就决定了必须有

一个其它的Structural ObjectClass才能定义一个Entry.其中ObjectClass又可以存在继承关系,子ObjectClass会继承父ObjectClass中的

全部Attribute.该继承关系于Java中有点相似.

在LDAP中每一个ObjectClass都定义了一些Attribute,其Attribute仍然可以是ObjectClass。在这些Attriubte中分为两种类型MUST,MAY, MUST表示这个Entry必须包括的属性,MAY为可选。一个ObjectClass的Attribute也包括所有继承自父ObjectClass和自身定义的ObjectClass。

下面用一个类型进行说明:

objectclass ( 2.5.6.0 NAME 'top' ABSTRACT

MUST objectClass )

objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'

DESC 'RFC2247: domain component object'

SUP top AUXILIARY

MUST dc )

上面是两个objectclass的定义,其中top为ABSTRACT,dcObject为AUXILIARY,这两个类型都不能定义Entry.

下面这个LDIF文件在导入到LDAP时会出错:

dn: dc=java,dc=com

objectClass:dcObject

dc: java.com

要定义这个Entry必须找到一个STRUCTURAL类型的ObjectClass。

objectClasses: ( 2.5.6.4 NAME 'organization'

DESC 'RFC2256: an organization' SUP top STRUCTURAL

MUST o

MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory

$ x121Address $ registeredAddress $ destinationIndicator

$ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier

$ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber

$ street $ postOfficeBox $ postalCode $ postalAddress

$ physicalDeliveryOfficeName $ st $ l $ description ) )

这个objectClass的类型为STRUCTURAL,因此可以用来定义Entry.具体定义如下

dn: dc=java,dc=com

objectClass:dcObject

objectClass:organization

dc: java.com

o: java.com

Attribute

Attribute类同于编程语言中的变量,它可以被赋值,就像是可以存放一个单一类型信息的容器。官方声明了许多常用的 Attribute,如果其中没有你所需要的,你可以自己定义,但要避免重名。objectClass是一种特殊的Attribute,它包含其它用到的 Attribute以及它自身。常见的Attribute如下:

schema

LDAP中,schema用来指定一个目录中所包含的objects的类型 (objectClass)以及每一个objectClass中的各个必备(mandatory)和可选(optional)的属性 (attribute)。因此,Schema是一个数据模型,它被用来决定数据怎样被存储,被跟踪的数据的是什么类型,存储在不同的Entry下的数据之间的关系。schema 需要在主配置文件slapd.conf中指定,以用来决定本目录中使用到的objectClass。管理员可以自己设计制定schema,一般包括属性定 义(AttributeDefinition)、类定义(ClassDefinition)以及语法定义(SyntaxDefinition)等部分。

LDAP V3中在x.500标准的基础上定义了一个包含了网络中大多常见对象的schema,这些对象包括国家、所在地、组织、人员、小组以及设备等。同时,LDAP V3中可以很方便的从目录中提取出schema,它正是一条记录中关于属性的声明部分。

对象标识符

对象标识符( Object Identifiers )是被LDAP内部数据库引用的数字标识。Attribute的名字是设计为方便人们读取的,但为了方便计算机的处理,通常使用一组数字来标识这些对象,这类同于SNMP中的MIB2。例如,当计算机接收到dc这个Attribute时,它会将这个名字转换为对应的OID: 1.3.6.1.4.1.1466.115.121.1.26。

几点体会

重大误解

很多所谓基于LDAP实现的认证都是使用目录来存储加密(多数是MD5)后的密码而已.但是这种密码很好被暴力破解.于是你的系统的安全状态有退回到未 shadow过的/etc/passwd时代.在介绍目录服务的书籍中提到/etc/passwd也算是一种目录.我们不能说一个文本文件是用来做认证的吧?

很多LDAP的实现和应用都不使用LDAP来作验证.如微软的活动目录.还有斯坦福大学的目录都使用了kerberos.配置好的Windows域控制器本身就是一个Kerberos KDC.而斯坦福的目录也采用了Hedimal的Kerberos实现来做KDC和一个Windows 的KDC.这样的理由很明显的.如果要通过过LDAP目录来实现一个十分安全的认证信息的存储要借助大量的外围协议.还不如直接使用这些协议本身.长话短说,我们应该让目录存储它改存储的东西.

产品选择

产品的选择取决于你部署目录服务的目的以及内容.如果活动目录提供给你的那些的功能足够你的需要.那么采用活动目录好了.因为他一开始就将安全实现的很好(操作系统安全不在讨论之列).而且配置简单.而且和客户端(windows)兼容的较好.

如果你想在深入研究LDAP协议.或者要实现一个高度自定义的目录.那么选择OpenLDAP吧!

安全措施.

尽量少将安全敏感的信息存储在目录中.如果非存不可.一定要编辑ACL严格的控制访问权限如userPassword等.多数情况下目录服务还要主要是给其他的服务提供数据存储.这样的话应该让每个服务器绑定到不通的DN,同时在ACL中赋予他们较高的权限.而不能让所有的服务都使用rootdn来绑定.

目录结构的设计

虽然目录结构的设计可以比较灵活.但比较有经验的设计可以使你的目录结构更加合理,而且可以给你带来意想不到的好处.尽量参照前人实现的产品一吸收好的思想.分析微软的设计,在目录中建立分组的目录树是明智的做法.这样每个帐户的memberof属性的值他所属的组的DN,这样就可以方便的将基于组的访问控制列表也实现在目录结构中.相象如果活动目录中用户的memberof属性是"Aministrators"而不是"CN= Administrators,CN=Builtin,DC=example,DC=edu,DC=cn"的话会给微软带来多少麻烦?尽量的将更多的数据以树的形式组织,同时在字段中使用更多的DN代替单一的数值,可以最大限度的发挥目录服务的潜力.
AD和LDAP的关系

Active  Directory应该是LDAP的一个应用实例,而不应该是LDAP本身。比如:windows域控的用户、权限管理应该是微软公司使用LDAP存储了一些数据来解决域控这个具体问题,
只是AD顺便还提供了用户接口,也可以利用Active Directory当做LDAP服务器存放一些自己的东西而已。比如LDAP是关系型数据库,微软自己在库中建立了几个表,每个表都定义好了字段。显然这些表和字段都是根据微软自己的需求定制的,而不是LDAP协议的规定。然后微软将LDAP做了一些封装接口,用户可以利用这些接口写程序操作LDAP,使得Active Directory也成了一个LDAP服务器。
总之:Active Directory = LDAP服务器+LDAP应用(Windows域控)。Active Directory先实现一个LDAP服务器,然后自己先用这个LDAP服务器实现了自己的一个具体应用(域控)

参考至:http://doc.linuxpk.com/2293.html
             http://doc.linuxpk.com/2583.html 
             http://doc.linuxpk.com/2586.html 
             http://doc.linuxpk.com/28400.html
             http://bbs.chinaunix.net/thread-889055-1-1.html 
             http://hi.baidu.com/dongyuejiang/item/d2af278125f05d5d26ebd9a0
             http://linux.chinaitlab.com/special/ldap/Index.html

java访问ad域的更多相关文章

  1. java修改AD域用户密码使用SSL连接方式

    正常情况下,JAVA修改AD域用户属性,只能修改一些普通属性, 如果要修改AD域用户密码和userAccountControl属性就得使用SSL连接的方式修改, SSL连接的方式需要操作以下步骤: 1 ...

  2. 使用java连接AD域,验证账号密码是否正确

    eb项目中有时候客户要求我们使用ad域进行身份确认,不再另外做一套用户管理系统.其实客户就是只要一套账号可以访问所有的OA,CRM等办公系统.这就是第三方验证.一般有AD域,Ldap,Radius,邮 ...

  3. java连接AD域

    import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.Hashtable; imp ...

  4. AD 域服务简介(三)- Java 对 AD 域用户的增删改查操作

    博客地址:http://www.moonxy.com 关于AD 域服务器搭建及其使用,请参阅:AD 域服务简介(一) - 基于 LDAP 的 AD 域服务器搭建及其使用 Java 获取 AD 域用户, ...

  5. AD 域服务简介(二)- Java 获取 AD 域用户

    博客地址:http://www.moonxy.com 关于AD 域服务器搭建及其使用,请参阅:AD 域服务简介(一) - 基于 LDAP 的 AD 域服务器搭建及其使用 一.前言 先简单简单回顾上一篇 ...

  6. 使用java连接AD域,验证账号password是否正确

    web项目中有时候客户要求我们使用ad域进行身份确认,不再另外做一套用户管理系统.事实上客户就是仅仅要一套账号能够訪问全部的OA.CRM等办公系统. 这就是第三方验证.一般有AD域,Ldap,Radi ...

  7. JAVA 通过LDAP获取AD域用户及组织信息

    因为工作需求近期做过一个从客户AD域获取数据实现单点登录的功能,在此整理分享. 前提:用户可能有很多系统的情况下,为了方便账号的统一管理使用AD域验证登录,所以不需要我们的系统登录,就需要获取用户的A ...

  8. JAVA使用Ldap操作AD域

    项目上遇到的需要在集成 操作域用户的信息的功能,第一次接触ad域,因为不了解而且网上其他介绍不明确,比较费时,这里记录下. 说明: (1). 特别注意:Java操作查询域用户信息获取到的数据和域管理员 ...

  9. AD域-让共享目录只显示用户有权限访问的文件夹

    问题: 在AD域中,我们一般都会用到共享,如果有很多部门,我们可能还会按部门.职位配置权限.比如CSD,IT,PA等,但文件夹一多,用户看着就头大,而且用户没权限访问的文件夹误点击进去还会提示无权限访 ...

随机推荐

  1. form表单 datalist 和legend

    <form action="" method="post" > <fieldset> <legend> 表单元素 </ ...

  2. LeetCode解题报告—— Reverse Nodes in k-Group && Sudoku Solver

    1. Reverse Nodes in k-Group Given a linked list, reverse the nodes of a linked list k at a time and ...

  3. redis之(四)redis的字符串类型的命令

    [一]获得符合规则的键名列表  -->命令  keys [pattern] -->keys命令需要遍历Redis中所有的键,当键的数量比较多会影响性能,生产环境不建议用 -->pat ...

  4. git reset用法

    git 删除 错误 提交的 commit 方法:         根据–soft –mixed –hard,会对working tree和index和HEAD进行重置:    git reset -- ...

  5. CentOS6.5修改/etc/pam.d/sshd后root无法ssh登陆

    现象:由于公司需要服务器的登陆操作进行安全加固,同事为了省事,直接把CentOS7上的/etc/pam.d/sshd替换掉CentOS6.5上的/etc/pam.d/sshd,导致root用户ssh登 ...

  6. LD_PRELOAD的妙用,让python支持自己编译的Sqlite

    LD_PRELOAD的妙用,让python支持自己编译的Sqlite LD_PRELOAD=/usr/local/sqlite/lib/libsqlite3.so.0 python3 -c " ...

  7. MediaPlayer滑动不准的问题

    因为MediaPlayer在seekto是异步进行的,如果在滑动过程中暂停,会导致滑动不准确的情况,这时候就需要添加滑动完成的监听即setOnSeekCompleteListener

  8. 洛谷P4331 [BOI2004] Sequence 数字序列 [左偏树]

    题目传送门 数字序列 题目描述 给定一个整数序列 a1​,a2​,⋅⋅⋅,an​ ,求出一个递增序列 b1​<b2​<⋅⋅⋅<bn​ ,使得序列 ai​ 和 bi​ 的各项之差的绝对 ...

  9. 洛谷——P1680 奇怪的分组

    P1680 奇怪的分组 题目背景 终于解出了dm同学的难题,dm同学同意帮v神联络.可dm同学有个习惯,就是联络同学的时候喜欢分组联络,而且分组的方式也很特别,要求第i组的的人数必须大于他指定的个数c ...

  10. java中byte取值范围为什么是 -128到127

    概念:java中用补码表示二进制数,补码的最高位是符号位,最高位为“0”表示正数,最高位为“1”表示负数.正数补码为其本身:负数补码为其绝对值各位取反加1:例如:+21,其二进制表示形式是000101 ...