Users代表系统的物理用户,是人们用来登录系统的用户账号。默认情况下,用户可以获得他们可以管理且具有公共和私有页面的私有站点,管理员可以关闭或者锁定该内容。但该私有空间很重要:让用户可以具有自己的公共blog或者私有的日历,个人文档存储等。
可以通过多种途径归集用户。他们可以是分层组织中的成员,例如Liferay中包含安全→因特网安全。用户可以归属于专门的用户群,例如博客群体,这样用户可以在他们的私人空间创建博客载体。他们也可以是有相同兴趣的站点当中的成员。用户也可以拥有自己的角色来描述他们在系统中的功能,这些角色的作用域可以使门户,组织,或者站点。
用户组是由管理员创建的简单、专属的用户集合。他们可以是站点或者角色中的成员。权限不能分配给用户组。尽管用户组不像其他的一些用户集合(例如站点或者组织)一样有页面集。但是它们具有可以用来定制用户私人页面集的页面模板。这在后面会详尽的描述。
Liferay有以下三种类型角色:
l 门户角色
l 组织角色
l 站点角色
这些叫做角色作用域。角色被用于在它的作用域范围内定义权限:作用于门户,作用于组织或者作用于站点。例如,考虑授权一个角色可以创建一个留言板,一个门户角色(Portal Role)允许在门户范围内访问,不管该留言板(Message Board)在门户的任何地方。一个站点角色仅授权用户对站点内的留言板有访问权限。一个组织角色也仅授权特定组织内的留言板有访问权限。
因为角色是严格用于门户安全的,因此它们不能像站点和组织一样拥有自己的页面。
用户、用户组、站点、组织都可以是角色的成员。
组织是分层的用户集合,它是两类可以有页面的portal资源之一。这里也有一个叫做location的特殊组织,可以用于定义用户所处的特殊位置。
组织能够方便的定义用户在一个特殊层次关系中的位置。例如,如果你为一个大型企业实现了Liferay门户,通过Joe Smith在组织结构图中的位置,它就可以帮助定义Joe Smith用户。假如Joe Smith是位于新泽西州办公室的一个销售工程师,在东北部的销售部工作,那么他可能是以下组织的成员:
l 销售
l 东北部
l 新泽西州
现在,假如在每个用户的home page上都放置了一个名叫Asset Publisher的静态portlet(通过用户组(User Group)页面模版实现),此时,就可以通过内容管理系统将许多公告信息通知到员工。只要内容标记合适,可以确保Joe Smith获得所有在东北部或者新泽西州和销售有关的公告。
组织可以是站点的成员。
站点是有共同兴趣的用户集合(在以前的版本中叫做社区),Liferay的默认页面是以门户名称命名的站点的一部分,因为不管是匿名用户还是门户用户,都有访问站点默认公共页面的需要。有三种不同类型的站点:
l 开放(open)
l 有限的(restricted)
l 私有的(hidden)
开放站点(默认)允许用户在任何时候都可以自由加入或者退出,使用控制面板或者一个站点的 portlet添加到他们可以访问的页面。有限站点中只有站点管理员可以增加用户。用户可以用Control Panel或者Site portlet申请用户.私有站点和有限站点类似,特别之处是他不会在Control Panel或者Site portlet里面显示。私有站点中必须是站点管理员才能增加用户。
Team在每个站点和组织中是独立存在的,Team本质上是在站点里面能够创建的用户集合。它和组织及站点角色不一样,Team仅仅出现在创建它的组织和站点里面。这对需要在一个站点或者组织里面为一些特殊目的创建一组用户但却不需要在门户中的每一个站点和组织中使用的情况非常有用。
Team同时也是某些应用场景中必不可少的,因为它们可以被站点或者组织管理员创建。站点和组织管理员不能创建角色,因此,通过team他们就可以在一定的级别进行权限管理,这是在没有Team之前做不到的。
各部分之间的关系,箭头可读作可属于
各部分之间的关系,箭头可读作可分配
各部分的存储表
Liferay的权限体系也是基于角色的权限控制,但是在谈Liferay的权限之前,需要了解的一个概念就是资源,在Liferay中所有的一切都被看做是资源,和权限直接打交道的就只有资源。以下内容都可以看作是资源:
§ Portlets:我们开发的portlet或者Liferay自带的Portlet(文档管理、公告、博客、WIKI)
§ Java类:例如每一个实体类,如user、group,及这些实体产生的数据,如一个公告信息、一篇博客信息
§ 文件:如一张图片、一个文档、一个视频等
§ 一个字段:如添加的自定义字段的具体的一个字段等
§ 其他:一个社区、一个站点、一个用户等
权限是在资源上面的动作控制,如果View权限在某一个Portlet上面,则是表示拥有查看某一个Portlet的权限,也就是用户对Portlet这个资源可以执行查看的动作。
如对一个资源的查询、编辑、修改、删除等都是在这个资源的上的动作。
在Liferay里面存储权限的表主要涉及两个表,一个叫resourcepermission(资源权限表)。此表定义了Liferay里面权限结构,里面的表结构主要字段含义如下:
§ Name:资源对象的描述,如果资源描述的是一个portlet对象,则为这个portlet对象的portletId.如果是一个class, 则为带包名的class全名称
§ Scope:权限的作用域,值有1、2、3、4,1表示company级就是实例、2表示group级就是站点级,3是站点模板,4是没级,就表示当前的。
§ Primkey:资源的ID
§ Roleid:角色id
§ Ownerid:资源的属于哪一个人,一般值为0或创建的用户id
§ Actionids:这里的actionids为resourceaction表里面的bitwisevalue的值的相加之和。
这里重点字段有四个:Name确定资源的类型是portlet还是具体的modle还是其他的,primkey为资源的主键,我们通过资源类型和资源ID就确定了一个具体的资源(具体某一个portlet,具体某一张图片等),Roleid确定的是角色,actionids这个确定的是具体的有哪些可执行动作(查看、修改、删除等等)。
另外一张表为:resourceaction(资源动作表),此表定义了Liferay中的相应的资源上可执行的动作有哪些。主要的表结构字段如下:
§ Name: 定义了资源的类型,同上面的表一样,确定资源是portlet还是实体或者是其他,表示是什么资源。
§ actionId:可执行的动作名称,这里主要是为我们的动作启一个名称,如添加、修改、配置等。
§ bitwisevalue:这个定义的为上面可执行动作的位值,也可以看作是权限的动作ID。
上面介绍完这两个表了,那确定一个权限的过程是什么呢?比如我们要判断某一个用户是否拥有welcome页面的查看(VIEW)权限:
1. 根据用户,获取用户的角色,我们可以知道某一个用户拥有哪些角色。这一步应该没有啥疑问。
2. 在资源权限表里面询name值为com.liferay.portal.model.Layout(表示页面的资源类型)的,primkey为页面的当前页面Id的。
3. 现在有了角色、有了页面,要判断的就是这个角色是否有对这个页面的view的可执行动作。我们发现在资源动作表(resourceaction)里name为com.liferay.portal.model.Layout,actionid为VIEW地记录中bitwisevalue的值为1,则我们现在只需要判断资源权限表(resourcepermission)里面的actionids是不是有1组成的即可。那怎么判断呢?
我们来观察一下这两张表的结构:
在资源权限表里面actionids这个里面的,这个里面的值我们看到的如是3(=1+2),如果是9(=1+8),如果是258(2+256),如果观察就会发现这里面的值总是等于2的n次方的值相加的结果。我们再来看资源动作表resourceaction,发现里面的bitwisevalue的值是1、2、4、8、16、32等这样的值,总是2的n次方。
现在我们要判断的就是actionids的值是不是有某一个2的2次方,比如3这个值,我们只需要判断他的组成部分是不是有1。如果本示例中我们只需要判断view是的bitwisevalue的值是不是在actionids里面,这个判断我们使用位运算。如
3&1 = 0000 0011&0000 0001(二进制)=0000 0001(二进制)=1
上面这个太简单,现在我们判断7里面是否包含4
7&4 = 0000 0111&0000 0100 = 0000 0100 = 4
所以我们如果要判断bitwisevalue的值是否在actionids里面,只需要两个做位运算,actionids|bitwisevalue是否等于bitwisevalue即可。
本篇文章只是介绍了Liferay的权限体系,在下一篇文章中介绍如何使用Liferay的权限。
收集一些使用Liferay实现的国内的网站,因为有许多是企业内部使用的系统,在这里写的仅限于在外网可以访问的,随便找了一些,不可能将所有的都写完
§ 贵州电网: http://www.gz.csg.cn/
§ 广州白云机场: http://www.gbiac.net/
§ 云南冶金集团:http://www.cymco.cn
§ 云迈软件官网:http://eonmain.com/
§ 云迈软件基于SaaS的CRM:http://example.eonmain.com/
§ 阆中市人民政府 :http://www.langzhong.gov.cn/
§ 拓维信息系统股份有限公司:http://www.mtalkweb.com/
§ 上海新闻网:http://www.sh.chinanews.com/
§ 大唐文山发电: http://www.cdt-ws.com/
§ 四川华迪信息技术有限公司:http://www.hwadee.com/
§ 聚财富:http://www.jucaifu.com/home
§ 澳门政府:http://portal.gov.mo/
§ 金华教育: http://www.jhedu.org/
§ 中山大学图书馆: http://library.sysu.edu.cn/
§ 西安翻译学院: http://59.75.8.103:8000/
§ 辽宁中医药大学:http://www.lnutcm.edu.cn/
§ 卧龙电气: http://www.wolong.com.cn/
§ 澳门政府特区: http://portal.gov.mo/
§ 云南瑞攀科技有限公司:http://www.eruipan.com
§ 云企APP联盟:http:/www.rayapp.net
§ 东莞教育网:http://www.dgjy.net/
§ 行知汇元培训中心:http://www.11fit.com/
§ 中国国家数字图书馆:http://mylib.nlc.gov.cn/
§ 湖北省高等教育数字图书馆:http://www.hbdlib.cn/web/guest/home
§ 昆钢控股:http://www.ynkg.com/
§ 菲时特集团:http://www.ppw.cn/
§ 大连职业技术学院:http://www.dlvtc.edu.cn
§ 大连地方税务局纳税人学校:http://nsrschool.dl-l-tax.gov.cn
§ 云南电网:http://www.yn.csg.cn/
§ 西安交通大学校友网:http://alumni.xjtu.edu.cn/web/guest
§ 中国科学院生物研究所图书信息平台:http://159.226.163.150:8080
§ 评估资讯网:http://pingguw.com/
§ 中国科学院空间中心研究生部:http://159.226.23.22
§ 郑州大学校园卡网:http://ecard.zzu.edu.cn
§ 湖南工程学院:http://www.hnie.edu.cn/
§ 北京温州企业商会:http://www.world-wz.com
§ 中科天翔:http://www.g-soft.com.cn
§ 北京校际通信信息技术有限公司:http://www.51edu.com.cn
§ 中国音乐学院教务在线:http://jwc.ccmusic.edu.cn/
§ 北京恒邦凯捷散热器有限公司:http://www.zssm.com.cn
§ 南昌大学教务在线:http://jwc.ncu.edu.cn
§ 华滨手机网:http://www.huabin.net
§ 中国医药大学附设医院:http://www.cmuh.cmu.edu.tw
§ 厦门建发旅游集团:http://www.cndtour.com
§ 云南泛亚物流集团:http://www.ynfy56.com/
§ 湖南国科微电子有限公司:http://203.93.211.7
§ 昂科信息技术有限公司:http://www.ocamar.com
美国海军陆战队:www.marines.com
搭建了一个Hadoop的环境,Hadoop集群环境部署在几个Linux服务器上,现在想使用windows上的Java客户端来操作集群中的HDFS文件,但是在客户端运行时出现了如下的认证错误,被折磨了几天,问题终得以解决。以此文记录问题的解决过程。
(如果想看最终解决问题的方法拉到最后,如果想看我的问题解决思路请从上向下看)
上传文件的代码:
1. private static void uploadToHdfs() throws FileNotFoundException,IOException {
2. //我的文件地址
3. String localSrc = "E:\\快盘\\技术文档\\hadoop\\HDFS初步研究.pdf";
4. //存放在云端的目的地址
5. String dest = "hdfs://192.168.2.156:9000/user/HDFS初步研究.pdf";
6. InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
7. //得到配置对象
8. Configuration conf = new Configuration();
9. // conf.set("fs.default.name","hdfs://192.168.2.156:9000");
10. //文件系统
11. FileSystem fs = FileSystem.get(URI.create(dest), conf);
12. //输出流
13. OutputStream out = fs.create(new Path(dest), new Progressable() {
14. @Override
15. public void progress() {
16. System.out.println("上传完一个设定缓存区大小容量的文件!");
17. }
18. });
19. //连接两个流,形成通道,使输入流向输出流传输数据
20. IOUtils.copyBytes(in, out, 4096, true);
21. }
错误的详细描述如下:
org.apache.hadoop.security.AccessControlException: org.apache.hadoop.security .AccessControlException: Permission denied: user=Administrator, access=WRITE, inode="hadoop": hadoop:supergroup:rwxr-xr-x
其实这个错误的原因很容易看出来,用户Administator在hadoop上执行写操作时被权限系统拒绝.
看到这个错误的,第一步就是将这个错误直接入放到百度google里面进行搜索。找到了N多篇文章,但是主要的思路就如此篇文章所写的两个解决办法:http://www.cnblogs.com/acmy/archive/2011/10/28/2227901.html
1、在hdfs的配置文件中,将dfs.permissions修改为False
2、执行这样的操作 hadoop fs -chmod 777 /user/hadoop
对于上面的第一个方法,我试了行不通,不知道是自己设置错误还是其他原因,对我此法不可行,第二个方法可行。第二个方法是让我们来修改HDFS中相应文件夹的权限,后面的/user/hadoop这个路径为HDFS中的文件路径,这样修改之后就让我们的administrator有在HDFS的相应目录下有写文件的权限(所有的用户都是写权限)。
虽然上面的第二步可以解决问题了,上传之后的文件所有者为Administrator,但是总感觉这样的方法不够优雅,而且这样修改权限会有一定的安全问题,总之就是看着不爽,就在想有没有其他的办法?
开始仔细的观察了这个错误的详细信息,看到user=Administrator, access=WRITE。这里的user其实是我当前系统(运行客户端的计算机的操作系统)的用户名,实际期望这里的user=hadoop(hadoop是我的HADOOP上面的用户名),但是它取的是当前的系统的用户名,很明显,如果我将当前系统的用户名改为hadoop,这个肯定也是可以行得通的,但是如果后期将开发的代码部署到服务器上之后,就不能方便的修改用户,此方法明显也不够方便。
现在就想着Configuration这个是一个配置类,有没有一个参数是可以在某个地方设置以哪个用户运行呢?搜索了半天,无果。没有找到相关的配置参数。
最终只有继续分析代码, FileSystem fs = FileSystem.get(URI.create(dest), conf);代码是在此处开始对HDFS进行调用,所以就想着将HADOOP的源码下下来,debug整个调用过程,这个user=Administator是在什么时间赋予的值。理解了调用过程,还怕找不到解决问题的办法么?
跟踪代码进入 FileSystem.get-->CACHE.get()-->Key key = new Key(uri, conf);到这里的时候发现key值里面已经有Administrator了,所以关键肯定是在new key的过程。继续跟踪UserGroupInformation.getCurrentUser()-->getLoginUser()-->login.login()到这一步的时候发现用户名已经确定了,但是这个方法是Java的核心源码,是一个通用的安全认证,但对这一块不熟悉,但是debug时看到subject里面有NTUserPrincipal:Administator,所以就想着搜索一下这个东西是啥,结果就找到了下面这一篇关键的文章:
http://www.udpwork.com/item/7047.html
在此篇文章里面作者分析了hadoop的整个登录过程,对于我有用的是其中的这一段:
2.login.login();
这个会调用HadoopLoginModule的login()和commit()方法。
HadoopLoginModule的login()方法是一个空函数,只打印了一行调试日志 LOG.debug("hadoop login");
commit()方法负责把Principal添加到Subject中。
此时一个首要问题是username是什么?
在使用了kerberos的情况下,从javax.security.auth.kerberos.KerberosPrincipal的实例获取username。
在未使用kerberos的情况下,优先读取HADOOP_USER_NAME这个系统环境变量,如果不为空,那么拿它作username。否则,读取HADOOP_USER_NAME这个java环境变量。否则,从com.sun.security.auth.NTUserPrincipal或者com.sun.security.auth.UnixPrincipal的实例获取username。
如果以上尝试都失败,那么抛出异常LoginException("Can’t find user name")。
最终拿username构造org.apache.hadoop.security.User的实例添加到Subject中。
看完这一段,我明白了执行login.login的时候调用了hadoop里面的HadoopLoginModule方法,而关键是在commit方法里面,在这里优先读取HADOOP_USER_NAME系统环境变量,然后是java环境变量,如果再没有就从NTUserPrincipal等里面取。关键代码为:
1. if (!isSecurityEnabled() && (user == null)) {
2. String envUser = System.getenv(HADOOP_USER_NAME);
3. if (envUser == null) {
4. envUser = System.getProperty(HADOOP_USER_NAME);
5. }
6. user = envUser == null ? null : new User(envUser);
7. }
OK,看到这里我的需求也就解决了,只要在系统的环境变量里面添加HADOOP_USER_NAME=hadoop(HDFS上的有权限的用户,具体看自己的情况),或者在当前JDK的变量参数里面添加HADOOP_USER_NAME这个Java变量即可。我的情况添加系统环境变量更方法。
如果是在Eclipse里面运行,修改完环境变量后,记得重启一下eclipse,不然可能不会生效。
最终,总结下来解决办法大概有三种:
1、在系统的环境变量或java JVM变量里面添加HADOOP_USER_NAME,这个值具体等于多少看自己的情况,以后会运行HADOOP上的Linux的用户名。(修改完重启eclipse,不然可能不生效)
2、将当前系统的帐号修改为hadoop
3、使用HDFS的命令行接口修改相应目录的权限,hadoop fs -chmod 777 /user,后面的/user是要上传文件的路径,不同的情况可能不一样,比如要上传的文件路径为hdfs://namenode/user/xxx.doc,则这样的修改可以,如果要上传的文件路径为hdfs://namenode/java/xxx.doc,则要修改的为hadoop fs -chmod 777 /java或者hadoop fs -chmod 777 /,java的那个需要先在HDFS里面建立Java目录,后面的这个是为根目录调整权限。