一、站点内的用户信息存储
1.1、Users、AllUsers、SiteUsers,它们有什么区别呢?
Users 集合是三个集合中包含成员最少的集合,该集合包含了当前网站中所有已显式分配了权限的外部主体。
AllUsers 集合包括 Users 集合中的所有成员,以及通过组或角色成员资格使用隐式权限访问过网站中的对象的外部用户。
SiteUsers 集合是定义在站点集层次上的用户,包含了当前网站集中每个网站的 AllUsers 集合的成员的一个聚合。该集合的成员包括所有已分配了对网站集中所有对象的访问权限的外部主体,以及所有已被授予使用隐式权限访问网站集中所有对象的权限的外部用户。
例1:由于用户组是属于站点集的,所以当我们在任意一个站点中进行操作,为一个用户组中添加一个用户时,这个用户都是被添加到站点集层次用户组中,也就是说,你在SPWeb.Users中是肯定找不到这个用户的。你可以在SPWeb.SiteUsers中找到他(因为这个用户是被添加到站点集层次的用户组中,所以他算一个站点集层次的用户),还可以在SPWeb.AllUsers中找到他(因为这个用户确实对这个站点具有访问权限)。
例2:假定名为 Brian 的用户(登录名 LITWAREINC\BrianC)从未被显式授予访问某个网站和查看特定列表的权限。但他也许仍可以查看列表,因为他所属的 Active Directory 组已被配置了列表查看权限。当 Brian 首次访问网站或其中任一对象(比如,使用隐式权限查看一个列表)时,他会被添加为 AllUsers 集合的成员,但不会被添加为 Users 集合的成员。
注意:一般我们在SharePoint上面添加用户的时候,都可能会用AD组的方式添加。所以,通常情况下,如果要取得所有用户信息(包括通过组的方式添加的那些用户),我们会用AllUsers。
但这里有一个关键:
如果一个用户是通过组的方式添加到网站的,那么当然他是否访问到该网站的。但也只有当他至少访问过一次该网站之后,我们才可以在Web.AllUsers中检索到他。但是,假设我们无法确认用户是否至少登陆过一次,那么有没有办法根据一个用户名或者邮件地址,获取到该用户的信息呢?你可能会想到,实际上这需要查询到AD中的信息。没错,但我们应该怎么来完成这样的任务呢?可以通过代码根据邮件地址查询用户信息:
var user = SPUtility.ResolveWindowsPrincipal(this.Site.WebApplication, "test@xizhang.com", Microsoft.SharePoint.Utilities.SPPrincipalType.User, true); if (user != null) Literal1.Text = string.Format("UserName:{0},LoginName:{1},Email:{2}", user.DisplayName, user.LoginName, user.Email); else Literal1.Text = "The user is not exist.";
1.2、"User Info List”、UserInfo表、 SiteUserProfileList
"User Info List”:查看网站集中所有用户信息,在2010中该列表被隐藏了,可以通过siteurl/_catalog/users,打开站点集所有用户列表。用户信息在这里只能查看,不能编辑。如果通过API,直接编辑该列表的信息的话,值会保存在AllUserData表中,而不是UserInfo表中。
UserInfo:如果一个新用户被添加到站点,则首先会被记录在UserInfo表中,再由用户信息同步程序同步到服务器场的User Profile库中。
SiteUserProfileList:由名字可以知道它的功能了,可以由web.SiteUserProfileList获得,如果要改站点内的用户信息,可以使用该对象。不过会在下次完全同步时将该信息覆盖。如果不想被覆盖,可以修改管理中心属性影射。
二、User Profile 同步规则
简单来说AD -> User Profile <-> Site User Info。
当把用户第一次添加到SharePoint站点中时,SharePoint自动以用户的AD信息为基础,来写入到UserInfo表中。然后,会将存储到SharePoint站点中的用户信息同步到User Profile中。同时,SharePoint Server还会自动定时的,将用户在User Profile中的信息,同步到各个SharePoint站点。这种同步是双向的。但是由于用户实际上不能在SharePoint站点中修改用户信息,所以实际上,这种同步绝大部分时候,都是将User Profile中的用户信息推送到SharePoint站点中。
同步过程是由2个SharePoint计时器作用(Timer Job)来完成的,“配置文件同步”(进行完全同步)和“配置文件快速同步”(进行差量同步):
用户信息从AD导入到SharePoint用户配置文件,再由上述TimerJob同步到各个网站集。
"Profile Synchronization",每小时运行一次。
这个timer job不需要管理员做任何操作,它是SharePoint预先设置好的,每小时运行的。如果你等不及1小时,需要马上测试这个(或者其他的某个)job的运行结果,那得通过stsadm或者对象模型临时改变这个job的运行周期。
由于SharePoint Server是以User Profile来作为用户信息存储中心,所以SharePoint Server不允许用户直接修改存储在SharePoint站点中的用户信息,而是定时自动的将User Profile中的用户信息推送到每个站点中。也就是说,即使你手工修改了UserInfo表中存储的用户信息数据,或通过SPUser对象模型修改了用户的属性信息,但它们很可能很快就会又被自动的覆盖成User Profile中的值。我们应该通过修改User Profile用户信息,来达到更新和维护用户信息的目的。User Profile应该是整个SharePoint Server服务器场中唯一的用户信息存储中心。
特别说明1:如何手工来快速进行将User Profile用户信息同步到SharePoint站点用户信息的过程?
尝试在服务器上手工执行下面两个stsadm指令:
stsadm -o execadmsvcjobs
stsadm -o sync
特别说明2:为什么执行了同步过程,不能同步某些用户在SharePoint站点中的信息?
因为配置文件同步操作,只会同步那些SharePoint认为是SharePoint站点“活动用户”。一个用户是否是活动用户由UserInfo表中的某个字段来标识。当一个用户在站点中进行某些“确实的”操作时,比如上传一个文件,他就会自动被标记为“活动用户”。
三、用户信息与AD用户信息的“不一致”问题
解决方法就是把UserInfo表的“td_SystemID”列的数据和当前AD中的用户信息进行同步。在SqlServer中有一个SUSER_SID()函数,可以返回某用户的SID信息。在Dean的Blog上,他已经给出了一个完整的Sql语句,大家可以直接使用。
顺便题一下,在Deam的Blog上,他也描述了这个问题所造成的另外一个问题:如果我们备份站点的时候,将用户信息也一起备份了下来,在恢复到另外一个AD中时,如果站点用户有同名的现象,也同样会造成SharePoint“不认”这个AD用户。
Sql语句:
DECLARE @login varchar(40), @systemid varbinary(128)
DECLARE curUsers CURSOR LOCAL FOR
SELECT tp_login, tp_systemid FROM userinfo where tp_deleted = 0
OPEN curUsers
FETCH NEXT FROM curUsers INTO @login, @systemid
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Resetting user ' + @login + ' to new SID '
PRINT suser_sid(@login)
UPDATE UserInfo
SET tp_systemid = suser_sid(tp_login) WHERE CURRENT OF curUsers
FETCH NEXT FROM curUsers INTO @login, @systemid
END
CLOSE curUsers
DEALLOCATE curUsers
GO