2012年5月31日星期四

ADF_128:使用SecurityContext.getUserRoles()获取用户的角色和组信息

运行环境:JDeveloper 11.1.2.4.0 + Oracle Database 11gR2 Express Edition。

本实验的目的是比较Container Security和ADF Security API,本实验基于《为ADF Web应用增加安全》。

1. 在products.jsf中增加如下代码:

<af:inputText label="User" id="it1" value="#{securityBean.currentUser}"/>
<af:inputText label="Groups" id="it2" value="#{securityBean.currentUserGroups}"/>
<af:inputText label="Roles" id="it3" value="#{securityBean.currentUserRoles}"/>
<af:inputText label="Belong To WLS Group: users ?" id="it4" value="Got the users role from weblogic"
              rendered="#{securityBean.wlsUserGroup}" columns="80"/>
<af:inputText label="Belong To WLS Role: valid-users" id="it5" value="Got the valid-users mapped by weblogic.xml"
              rendered="#{securityBean.containerUserRole}" columns="80"/>

2. 对应的SecurityBean.java代码如下:

package view;

import java.security.Principal;

import java.util.ArrayList;
import java.util.Set;

import javax.faces.context.FacesContext;

import javax.security.auth.Subject;

import oracle.adf.share.ADFContext;

import weblogic.security.Security;
import weblogic.security.SubjectUtils;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;

public class SecurityBean {
    private String user = null;
    private ArrayList groups = new ArrayList();

    public SecurityBean() {
        Subject subject = Security.getCurrentSubject();
        Set allPrincipals = subject.getPrincipals();
        for (Principal principal : allPrincipals) {
            if (principal instanceof WLSGroupImpl) {
                System.out.println("Found WLS Group: " + principal.getName());
                groups.add(principal.getName());
            }
            if (principal instanceof WLSUserImpl) {
                System.out.println("Found WLS User: " + principal.getName());
                user = principal.getName();
            }
        }
    }


    public String getCurrentUserGroups() {
        String curGroups = "";
        for (String group : groups) {
            curGroups = curGroups + ", " + group;
        }
        return curGroups;
    }

    public String getCurrentUserRoles() {
        ADFContext adfctx = ADFContext.getCurrent();
        String[] roles = adfctx.getSecurityContext().getUserRoles();
        String curRoles = "";
        for (String role : roles) {
            curRoles = curRoles + ", " + role;
        }
        return curRoles;
    }


    public boolean isWlsUserGroup() {
        for (int i = 0; i < groups.size(); i++) {
            if ("users".equalsIgnoreCase(groups.get(i))) {
                return true;
            }
        }
        return false;
    }

    public boolean isContainerUserRole() {
        if (FacesContext.getCurrentInstance().getExternalContext().isUserInRole("valid-users")) {
            return true;
        }
        return false;
    }

    public String getCurrentUser() {
        return user;
    }
}

3. 运行
使用sking/welcome1登录,发现SecurityContext.getUserRoles()返回的是sking所属的组和角色信息,而不只是角色信息,这一点和我们想的有些出入。


Project 下载:SecureApplication(getUserRoles).7z

参考文献:
1. http://blog.whitehorses.nl/2010/01/29/weblogic-web-application-container-security-part-1/
2. http://blog.whitehorses.nl/2010/02/01/weblogic-web-application-container-security-part-2-adf-security/

2012年5月30日星期三

ADF_127:使用userGrantedResource和userGrantedPermission表达式

运行环境:JDeveloper 11.1.2.1.0 + Oracle Database 10g Express Edition 10.2.0.1。

本来想在上一个实验的基础上继续做本实验,但是遇到一些问题,因此切换到JDeveloper 11.1.2.1.0开发环境。
不知道是不是因为JDeveloper 11.1.1.5的Bug或者因为使用了Database作为Policy Store。待查。
本实验在建立《为ADF Web应用增加安全》的基础之上。

重点步骤说明:

1. 设置ProductBase EO的安全
说明:实体的安全是扩展ADFPermission类而来的。
(1)打开read、update、delete的权限。

(2)给staff授予read权限。

(3)给supplier授予read、update、delete权限。

(4)修改edit.jsf页面,增加一个按钮:TestEnitityPermission,绑定相应的EL。
实体的安全是扩展ADFPermission类而来的,对应的EL写法如下:
<af:commandButton text="TestEnitityPermission" id="cb7" disabled="#{!securityContext.userGrantedPermission['permissionClass=oracle.adf.share.security.authorization.EntityPermission;target=model.ProductsBase;action=update']}"/>

2. 增加自定义的ResourcePermission
这个特性意味着你可以增加自己的Permission,你能想出的灵活的权限也就是这样了吧!
(1)在Resource Grants界面,点击Resource Type旁边的绿色的加号,增加一个ResourcePermission:
这里仅仅是出于测试的目的,自定义一个ButtonPermission,实际开发中不需要为Button专门定义Permission。
因为完全可以用已有的EL表达式绑定到Button的各个属性。

(2)把ButtonPermission权限授予supplier角色:

(3)修改edit.jsf页面,增加一个按钮:TestResourcePermission,绑定相应的EL。
自定义的ResourcePermission是扩展ResourcePermission类而来的,对应的EL写法如下:
<af:commandButton text="TestResourcePermission" id="cb8" disabled="#{securityContext.userGrantedResource['resourceType=ButtonPermission;resourceName=Button;action=disable']}"/>
注意,这个表达式的意思是拥有disable的角色返回true,因此就会disable这个Button。

3. 使用不同的用户登录测试
(1)使用achen登录,achen拥有supplier角色,因此achen拥有read、update、delete ProductBase实体对象和disable ButtonPermission的权限。

可以看出achen可以点击Delete和TestEntityPermission按钮,不可以点击TestResourcePermission按钮。
(2)使用sking登录,sking拥有staff角色,因此sking只拥有read ProductBase实体对象的权限。

可以看出sking不可以点击Delete和TestEntityPermission按钮,可以点击TestResourcePermission按钮。

4. 如何使用程序判断用户是否具有某个Permission?
(1)使用程序判断用户是否具有某个ResourcePermission?
public void testResourcePermission() {
String resourceType = "ButtonPermission";
String resourceName = "Button";
String action = "disable";

SecurityContext securityCtx = ADFContext.getCurrent().getSecurityContext();
ResourcePermission resourcePermission = new ResourcePermission(resourceType, resourceName, action);
boolean userHasPermission = securityCtx.hasPermission(resourcePermission);
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$ ResourcePermission " + userHasPermission);
}

(2)使用程序判断用户是否具有某个ADFPermission,如EntityPermission?
public void testEntityPermission() {
String target = "model.ProductsBase";
String action = "update";

SecurityContext securityCtx = ADFContext.getCurrent().getSecurityContext();
EntityPermission entityPermission = new EntityPermission(target, action);
boolean userHasPermission = securityCtx.hasPermission(entityPermission);
System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$ EntityPermission " + userHasPermission);
}
(3)运行edit.jsf,分别使用不同用户登录,并点击TestPermission按钮
使用用户achen登录后,点击TestPermission按钮,输出了两个true。
使用用户sking登录后,点击TestPermission按钮,输出了两个false。

Project 下载:SecureApplication(userGrant).7z

2012年5月20日星期日

ADF_126:使用数据库作为Policy Store

开发环境:JDeveloper 11.1.1.5 + RCU 11.1.1.5 + WebLogic 10.3.5 + Application Development Runtime 11.1.1.5。

Security Policy设计时是保存在jazn-data.xml文件中的,运行时则是保存在system-jazn-data.xml文件中。
在测试和生产环境中,保存在文件中的Policy显然在性能和扩展性(比如集群)方面无法满足要求。
Oracle目前提供了三种保存Policy的方式:文件、OID(Oracle Internet Directory)和Oracle Database。
考虑到大多数用户没有使用OID,这里介绍如何使用Oracle Database作为Policy Store。

重要步骤说明:

1. 备份文件:boot.properties、jps-config.xml和system-jazn-data.xml
有关安全方面的操作一不小心就可能导致服务器无法启动,所以要首先备份。
boot.properties位于[domain_name]\servers\AdminServer\security目录下。
jps-config.xml和system-jazn-data.xml位于[domain_name]\config\fmwconfig目录下。

2. 使用RCU创建OPSS Schema
选中Oracle Platform Security Services,注意Metadata Services会被自动选中。

其它步骤请参考《创建基于数据库的MDS》。

3. 使用WLS Console为OPSS Schema创建Data Source
注意,Data Source的类型必须为Non-XA Driver且不支持Global Transaction。
不过,我这里使用的Oracle XE 10gR2,选择默认的XA Driver,经过实验,发现是可以的。
但是不保证其它版本的Oracle 数据库也可以。

其它重要配置参数如下:
JNDI name: jdbc/OPSS_DS。
user name/password: SZ_OPSS/welcome1。

4. 使用EM Console更改Security Policy Provider
(1)右键[domain_name],选择Security,选择Security Provider Configuration

(2)发现目前的Policy Store、Credntial Store、Keystore都是基于文件保存的:

(3)点击“Change Store Type",选择Database,配置如下:
Data Source JNDI Name: jdbc/OPSS_DS
Root DN: cn=SecurityStore,这个是随便给的,无需事先创建。
Create New Domain: 勾上,这里不是要创建一个Domain,其含义是创建一个新的Policy Store树节点。因为是第一次移植Policy,所以要选中。
Domain Name: adf_domain,这个不一定你创建的Domain的名称,可以是个逻辑名称。

点击OK后,开始移植Policy到数据库中,如果成功会显示下图:

重新查看Domain的Security Provider Configuration,发现已经改成Oracle Database类型了:


5. 查看SZ_OPSS Schema的表结构和数据
发现SZ_OPSS Schema名下有很多表,非常复杂,大概看了一下,发现JPS_DN表中保存了Application Roles数据:


6. 使用WLST增加/修改Policy
如果将来需要增加/修改一些Policy,可以使用WLST脚本。
wlst命令位于[fmw_home]\wlserver_10.3\common\bin目录下。
注意,实际实验中,发现在[fmw_home]\wlserver_10.3\common\bin目录下无法找到reassociateSecurityStore,报告如下错误:NameError: NameError: reassociateSecurityStore。
分析之后,发现是因为reassociateSecurityStore不在默认安装的WLS中,而是在安装了SOA的库中。
于是切换到[soa_home]\common\bin目录下,我这里是C:\Oracle\Middleware\Oracle_SOA1\common\bin。
(1)运行wlst
(2)连接到WebLogic Server:connect('weblogic', 'welcome1', 't3://localhost:7001')
(3)重新关联SecurityStore: reassociateSecurityStore(domain='adf_domain', servertype='DB_ORACLE', datasourcename='jdbc/OPSS_DS', jpsroot='cn=SecurityStore', join='false')
如果是新建的SecurityStore,join='false';如果是关联到已有的SecurityStore,join='true'。
之所以有join='true'这种情况,是考虑到一个Policy Store可能为多个Domain(包括Domain上部署的应用)服务。


注意,system-jazn-data.xml此时虽然没有用了,但是建议不要删除,因为有可能你还需要把Policy移植回来。

参考文献:
1. http://fusionsecurity.blogspot.com/2011/10/reassociation-business.html
2. http://redstack.wordpress.com/2011/10/29/soa11g-database-as-a-policy-store/
3. http://ofmcorner.blogspot.com/2010/02/using-wlst-with-oracle-fusion.html

2012年5月16日星期三

ADF_125:为Entity Object增加安全性

开发环境:JDeveloper 11.1.1.5 + RCU 11.1.1.5 + WebLogic 10.3.5 + Application Development Runtime 11.1.1.5。

ADF Security保护的对象粒度级别可以达到Entity Object的行数据对象或EO的某个属性。
我将在《使用Enterprise Manager为ADF应用动态授权》应用的基础上做一些改动。

重要步骤说明:

1. 使能Entity Object的安全性
默认情况下,EO是没有被保护的,因此需要首先Enable你要保护的EO。
(1)保护整个ProductsBase EO
选择EO的Security属性,然后选中Update和Delete,表明只有拥有相应权限的人才可以修改和删除该EO。

(2)保护ProductsBase EO的一个Attribute:ListPrice
选择EO的ListPrice Attribute,然后选中Update,表明只有拥有相应权限的人才可以修改该Attribute。


2. 给角色授权
(1)在结构窗口中右键选中ProductsBase EO,然后选择“Edit Authorization”

(2)将修改和删除ProductsBase EO的权限授予productManager角色

(3)在结构窗口中右键选中ProductsBase EO的Attribute:ListPrice,然后选择“Edit Authorization”

(4)将修改ListPrice Attribute的权限授予productManager角色


3. 创建edit.jspx页面,修改和删除ProductBase。
注意,edit.jspx页面代码中没有任何与安全有关的EL表达式,比如在ListPrice InputText组件上和Delete按钮上都没有特别的设置。
因为在本实验中要测试的是要EO本身提供的细粒度安全性。

4. 修改Application Policies。

可以看出,角色manager和productManager都可以访问edit.jspx页面。
但是只有productManager角色可以修改和删除ProductsBase EO以及修改ListPrice Attribute。

5. 测试
(1)首先使用用户jack登录,jack拥有productManager角色。

(2)然后使用用户maping登录,maping拥有manager角色。

可以看出,虽然maping是经理,jack是maping的手下,但由于jack是产品经理,所以只有jack可以修改和删除ProductsBase EO以及修改ListPrice Attribute,而maping不可以。

6. 小结
与其它安全框架只能保护URL资源不同,ADF Security可以保护ADF-BC中的EO对象,甚至是EO的某个属性。
另外,视图层不需要为此修改任何原有代码,也就是说,业务服务层的安全可以“自动冒泡”到视图层,这实在太棒了 !

Project 下载:SecureApplication_11gPS4(secure_eo).7z

参考文献:
1. http://www.oracle.com/technetwork/issue-archive/2012/12-jan/o12adf-1364748.html

2012年5月15日星期二

ADF_124:使用Enterprise Manager为ADF应用动态授权

开发环境:JDeveloper 11.1.1.5 + RCU 11.1.1.5 + WebLogic 10.3.5 + Application Development Runtime 11.1.1.5。

接着《部署ADF应用到独立的WebLogic Server上》,我使用EM为SecureApplication动态授权。

我们知道Application Roles是与应用密切相关的逻辑安全角色,它决定拥有该角色的用户或组的权限。
但是Application Roles是在设计时在JDeveloper中定义的,难道要求每次都把所有的Application Roles事先定义好吗?
即使事先把所有的Application Roles都定义好,将来总是可能要增加或修改Application Roles的权限,难道每次改动都要重新发布应用吗?
能不能把应用本身的逻辑和安全分开来?让开发应用的人只管开发,安全的事情交给运维人员?

ADF Security考虑到了以上所有问题,使用Enterprise Manager可以为ADF应用在运行时动态授权。

1. 动态增加Appliction Roles
(1)右键选择应用,选择Security,选择Appliction Roles

(2)可以看到应用目前的Appliction Roles列表

(3)点击Create按钮增加一个Application Role:manager
注意,Application Role的成员可以是Application Role,说明Application Role可以嵌套。
也就是一个Application Role想要拥有另一个Application Role的权限,只要把另一个Application Role加入到这个Application Role中就可以了。
成员还可以是Group,这个Group就是Enterprise Role,一般建议给Group授予Application Role。
成员还可以是User,说明可以直接为某个用户授予Application Role,当然一般不建议这样做。
在本实验中Group和Users信息都是从从WebLogic Server内嵌的LDAP中读取的。

从上图可以看出,manager角色有一个成员:managerGroup,在mangerGroup中有一个User:maping。
如果你希望managerGroup同时拥有staff角色,可以在staff角色的成员中也加入managerGroup。
也就是说,用户maping同时拥有staff角色和manager角色。
好,我现在新增了一个Application Role:manager,但是这个角色能做些什么事情呢?
下面我就给manager设置Policies。

2. 动态增加Application Policies
(1)右键选择应用,选择Security,选择Appliction Policies

(2)可以看到应用目前的Appliction Policies列表

(3)点击Create按钮增加一个Application Policy
选择应用中不同的资源类型和资源以及Permission Actions。

我给manager角色授予View stock.jspx页面的权限。
注意,这里也可以直接给Group和User授权,不过不建议这样做。还是把权限授予角色比较好。


看到这个授权界面,也许你会产生一个问题,我能不能创建自己的要保护的资源以及Permission Actions?
这是一个很好的问题,我研究之后将另文专述。

3. 测试
不用重启Server或应用,直接访问http://localhost:7001/SecureApplication/faces/welcome.jspx。
使用账户maping/welcome1登录,测试结果完全符合预期——maping同时具有staff和manager的角色权限。



4. 小结
与其它安全框架只能配置认证提供者不同,Enterprise Manager可以为ADF Web应用动态授权,这真是一个非常棒的特性!
应用的代码与安全的逻辑彻底分开了,我很好奇ADF Security是如何做到的,有机会应该看看这部分的源代码。

参考文献:
1. http://andrejusb.blogspot.com/2010/07/managing-adf-security-using-oracle.html
2. http://blog.whitehorses.nl/2010/01/29/weblogic-web-application-container-security-part-1/
3. http://blog.whitehorses.nl/2010/02/01/weblogic-web-application-container-security-part-2-adf-security/
4. http://andrejusb.blogspot.com/2010/05/migrating-security-policies-from.html
5. http://www.oracle.com/technology/products/jdev/tips/muench/credmig111100/index.html
6. http://andrejusb.blogspot.com/2009/01/practical-adf-security-deployment-on.html

ADF_123:部署ADF应用到独立的WebLogic Server上

开发环境:JDeveloper 11.1.1.5 + RCU 11.1.1.5 + WebLogic 10.3.5 + Application Development Runtime 11.1.1.5。

我以《Enable Security in a Fusion Web Application》为例来说明如何把该应用发布到独立的WebLogic Server上。
首先要完成该教程,并在JDeveloper中内嵌的WLS上运行成功,开发完成后的Project可以点击这里下载:SecureApplication_11gPS4.7z

独立的WebLogic Server必须是安装了ADF Runtime Library的,安装方法请参考《图解安装 Application Development Runtime》。

重要步骤说明:

1. 在JDeveloper中创建指向独立WebLogic Server的连接





2. 修改Application Module的Connection Type为JDBC Data Source
为了简单起见,我把Application Module中LocalConfig和ShareConfig的Connection Type都改成了JDBC Data Source。



3. 在WebLogic Server Console中创建相应的JDBC Data Source

注意,Data Source的JNDI Name要和Application Module中的一致。



4. 修改ViewController Prject的Deployment Profile
说明:这一步不是必须的,只是为了让应用的访问URL开起来更好看一些。


5. 修改Application的Deployment Profile
说明:这一步不是必须的,只是为了让应用的访问URL开起来更好看一些。

这一步的四个选项的含义如下:
(1)Auto Generate and Synchronize weblogic-jdbc.xml Descriptors During Deployment:是否同步weblogic-jdbc.xml中的jdbc连接配置,因为没有使用该文件,所以一般不选中。
(2)Application Policies:一般不覆盖已有的Policy,所以一般不选中。
(3)Credentials:一般应用没有自己的密码,比如Email账号等等,即使有一般也不覆盖已有的Credentials,所以一般不选中。
(4)Users and Groups:应用中的用户和组一般不发布到生产和测试环境中,所以一般不选中。
总之,以上四个选项完全可以通过WLS/EM Console来单独配置,所以一般不需要在发布应用时覆盖已有的配置。


6. 部署 Application
目前ADF应用必须以打成ear包方式部署,不能够以war包方式部署。
直接发布war包,会报一个错误,Caused by: java.lang.ClassNotFoundException: oracle.adf.library.webapp.ResourceServlet。
Oracle JDev小组成员Shay也是这样说的,见参考文献1,看来目前就是这样设计的。
为了简单起见,我这里从JDevleoper中直接把应用发布到Admin Server上。
在生产环境中,不建议这样做,最好打成ear包,然后通过控制台发布。




在Deploy过程中,会询问你MDS Repositories的位置,关于MDS,请参考《创建基于数据库的MDS》。
如果你没有配置MDS,会显示空白,如下图:

如果你配置了MDS,JDeveloper会自动识别目前有哪些MDS,并自动创建一个MDS partition,使用.ear应用的名字作为partition的名称。
同时,它会使用刚刚创建的MDS Data Source作为MDS database。如下图:


7. 部署时遇到的异常
(1)首次部署时遇到一个异常:
Weblogic Server Exception: weblogic.management.DeploymentException:
[J2EE:160149]Error while processing library references. Unresolved application library references, defined in weblogic-application.xml: [Extension-Name: oracle.webcenter.framework, Specification-Version: 11.1.1, exact-match: false].
检查后,发现是weblogic-application.xml多了如下内容,于是去掉。
<library-ref>
<library-name>oracle.webcenter.framework</library-name>
<specification-version>11.1.1</specification-version>
</library-ref>
<library-ref>
<library-name>oracle.webcenter.skin</library-name>
<specification-version>11.1.1</specification-version>
</library-ref>
(2)再次部署,又遇到了一个新的异常:
Weblogic Server Exception: weblogic.application.ModuleException: :oracle.mds.exception.MDSExceptionList:MDS-01329: 无法加载元素 "persistence-config"
检查后,发现是adf-config.xml多了如下内容,于是去掉。
<persistence-config>
<metadata-namespaces>
<namespace path="/persdef/"
metadata-store-usage="WebCenterFileMetadataStore"/>
<namespace path="/xliffBundles/"
metadata-store-usage="WebCenterFileMetadataStore"/>
</metadata-namespaces>
<metadata-store-usages>
<metadata-store-usage id="WebCenterFileMetadataStore"
default-cust-store="true" deploy-target="true">
<metadata-store class-name="oracle.mds.dt.persistence.stores.file.SrcControlFileMetadataStore">
<property name="metadata-path" value="../../mds"/>
</metadata-store>
</metadata-store-usage>
</metadata-store-usages>
</persistence-config>
重新部署,这次终于成功了。

8. 测试
因为这个ADF应用带有安全性,为了将测试的用户部署到独立的WLS上,修改Application的Deployment属性,选中Users and Groups:

重新部署后,会在WebLogic Server Console中看到这些用户。

访问http://localhost:7001/SecureApplication/faces/welcome.jspx。
以不同的用户登录测试,运行效果和部署在JDeveloper内嵌的WebLogic Server一样。

9. 如何把ADF应用发布到Managed Server上?
(1)首先在WLS Console中增加一个Managed Server。
(2)然后扩展ADF Domain,选择EM。
(3)启动Admin Server和Managed Server,访问EM Console,点击“Apply JRF Template” ,此步很重要,一定不要忘了,否则会有些奇怪的错误。
(4)别忘了把Data Source Target到Managed Server上。
(5)通过WLS Console,发布.ear。(通过JDeveloper发布,无法成功,总是报告无法连接)

10. 其它问题
(1)如何访问http://localhost:7001/SecureApplication时自动跳转到welcome.jspx页面?
修改web.xml,增加如下内容:
<welcome-file-list>
<welcome-file>/faces/welcome.jspx</welcome-file>
</welcome-file-list>
注意,由于Context root后面有faces,所以需要加上这个路径,否则无法访问。
(2)如何访问http://localhost:7001/时自动跳转到welcome.jspx页面?
首先修改web.xml如(1),然后修改weblogic.xml如下:
<weblogic-web-app>
<context-root>/</context-root>
</weblogic-web-app>
(3)发现WEB-INF/lib下有很多Jar包,能不能所有应用共用这些包,不要每个Web应用都包含这些Jar包?

经过实验,发现可以去掉,SecureApplication应用运行正常。

Project 下载:SecureApplication_11gPS4(StandaloneWLS).7z

参考文献:
1. http://jdevshay.wordpress.com/2008/11/14/common_pitfalls_when_deploying/