无状态EJB不了解web.xml中声明的角色

时间:2014-02-20 10:58:55

标签: glassfish ejb ejb-3.1 glassfish-4

我实际上遇到了角色传播问题,我需要帮助。 我正在使用Glassfish 4.0,我正在部署一个包含JAX-RS资源的战争和一个带有远程和本地视图的EJB用于测试目的。

我在我的web.xmlglassfish-web.xml部署描述符中声明了角色,链接到Glassfish中的文件域。 JAX-RS资源正确使用了这些角色,但EJB似乎没有看到它们。

我将向您展示我正在使用的文件,然后是我到目前为止测试的不同呼叫输出的结果。

TL / DR:很抱歉这篇文章太长了。请转到第II部分/测试2

第一部分:代码

glassfish-web.xml部署描述符

<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
    <context-root>/war-test-4</context-root>
        <security-role-mapping>
        <role-name>test</role-name>
        <group-name>test</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>test2</role-name>
        <group-name>test2</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>authenticated</role-name>
        <group-name>authenticated</group-name>
    </security-role-mapping>
</glassfish-web-app>

web.xml部署描述符

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>war-test-4</display-name>
    <servlet>
        <description>JAX-RS Tools Generated - Do not modify</description>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <load-on-startup>1</load-on-startup>
        <security-role-ref>
            <description>Test</description>
            <role-name>test</role-name>
            <role-link>test</role-link>
        </security-role-ref>
        <security-role-ref>
            <description>Auth users</description>
            <role-name>authenticated</role-name>
            <role-link>authenticated</role-link>
        </security-role-ref>
        <security-role-ref>
            <description>Test2</description>
            <role-name>test2</role-name>
            <role-link>test2</role-link>
        </security-role-ref>
    </servlet>
    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/jaxrs/*</url-pattern>
    </servlet-mapping>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admin Resources</web-resource-name>
            <description>Administration resources</description>
            <url-pattern>/jaxrs/*</url-pattern>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description>TEST</description>
            <role-name>test</role-name>
            <role-name>test2</role-name>
            <role-name>authenticated</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>test-realm</realm-name>
    </login-config>
    <security-role>
        <description>Test</description>
        <role-name>test</role-name>
    </security-role>
    <security-role>
        <description>Auth users</description>
        <role-name>authenticated</role-name>
    </security-role>
    <security-role>
        <description>Test2</description>
        <role-name>test2</role-name>
    </security-role>
</web-app>

无状态EJB SessionBeanTest.java

/**
 * Session Bean implementation class SessionBeanTest
 */
@Stateless(mappedName = "SessionBeanTest")
//@RolesAllowed({"authenticated"})
//@DeclareRoles({"authenticated","test","test2"})
public class SessionBeanTest implements SessionBeanRemote, SessionBeanLocal {

    @Resource
    private SessionContext sessionContext;

    @Override
    public String get() {
        return MessageFormat
                .format("EJB Call by :{0} authenticated? : {1} / test2 ? : {2} / test ? : {3}",
                        sessionContext.getCallerPrincipal().getName(),
                        sessionContext.isCallerInRole("authenticated"),
                        sessionContext.isCallerInRole("test2"), sessionContext.isCallerInRole("test"));
    }
}

我的JAX-RS服务AccessTest.java

@Path("access")
//@DeclareRoles({/*"authenticated","test",*/"test2"})
@Stateless
public class AccessTest {

    @Inject
    private SessionBeanLocal testBean;
    @GET
    @Path("1")
    public Response test(@Context HttpServletRequest req){
        return Response.ok(MessageFormat
                .format("JAX-RS Call by :{0} authenticated? : {1} / test2 ? : {2} / test ? : {3}",
            req.getUserPrincipal().getName(),
                    req.isUserInRole("authenticated"),
                    req.isUserInRole("test2"),
                    req.isUserInRole("test"))).build();

    }

    @GET
    @Path("2")
    public Response test2(){
        return Response.ok(testBean.get()).build();
    }
}

正如您可能已经注意到的那样,我在EJB和JAX-RS资源中都注释了@DeclareRoles@RolesAllowed注释。我的注册表中也有2个URI JAX-RS服务。一个是直接向用户提供信息和角色,另一个使用EJB来检索相同的信息。 如果用户已登录,则两者都应返回完全相同的输出。

第二部分:测试

现在,使用网络服务测试人员(Paw on Mac,基于Curl,非常有用!),我正在访问这两个URI:

测试1:没有用户登录

URI /jaxrs/access/1的输出,没有用户

HTTP/1.1 401 Unauthorized
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

URI /jaxrs/access/2的输出,没有用户

HTTP/1.1 401 Unauthorized
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

到目前为止,它似乎按计划工作,需要经过身份验证的用户才能访问该资源。 但看看第二次测试...

测试2:具有所有角色的用户已登录

用户具有所有角色

的URI /jaxrs/access/1的输出
HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

JAX-RS Call by :testadmin authenticated? : true / test2 ? : true / test ? : true

用户具有所有角色

的URI /jaxrs/access/2的输出
HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

EJB Call by :testadmin authenticated? : **false** / test2 ? : **false** / test ? : **false**

这是我不明白的部分。在web.xmlglassfish-web.xml中声明的角色不会传播到EJB,它位于同一个WAR项目中。

测试3:取消注释代码中的@DeclareRoles注释

我是否取消注释EJB或JAX-RS服务中的@DeclareRoles({"authenticated"}),我得到以下输出:

URI /jaxrs/access/1的输出,用户拥有所有角色,@DeclareRoles取消注释

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

JAX-RS Call by :testadmin authenticated? : true / test2 ? : true / test ? : true

URI /jaxrs/access/2的输出,用户拥有所有角色,@DeclareRoles取消注释

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.0  Java/Oracle Corporation/1.7)

EJB Call by :testadmin authenticated? : **true** / test2 ? : **false** / test ? : **false**

EJB只能看到我声明的角色,但JAX-RS服务会看到它们

第三部分:远程EJB调用

我还有一个纯Java客户端用于测试目的。这是:

public class Main {

    public static void main(String[] args) throws Exception {
        getRemoteService();
    }

    public static void getRemoteService() throws Exception {
        String host = "127.0.0.1";
        String port = "3700";
        Properties props = new Properties();
        props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
        props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
        props.setProperty("java.naming.factory.state", "com.sun.cobra.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
        props.setProperty("org.omg.CORBA.ORBInitialHost", host);
        props.setProperty("org.omg.CORBA.ORBInitialPort", port);

        Context amInitial = null;
        amInitial = new InitialContext(props);
        ProgrammaticLogin programmaticLogin = new ProgrammaticLogin();
        programmaticLogin.login("testuser2", "password");
        SessionBeanRemote service = (SessionBeanRemote) amInitial.lookup("SessionBeanTest");
        System.out.println(service.get());
        programmaticLogin.logout();
        programmaticLogin.login("testadmin", "password");
        System.out.println(service.get());
    }
}

此客户端使用ProgrammaticLogin接口登录并将EJB与CORBA一起使用。除了测试之外,我不打算使用它。

首先,客户端将使用受限用户登录,然后使用具有所有角色的用户登录。 以下是使用此客户端的测试结果:

测试1:使用@DeclareRoles注释

测试远程EJB
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false

测试2:使用@DeclareRoles({"authenticated","test","test2"})未注释的

测试远程EJB
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false

此处,用户也未经过身份验证。但是当我向EJB添加@RolesAllowed注释时,我得到了这个:

测试3:使用@DeclareRoles@RolesAllowed({"authenticated"})添加

测试远程EJB
EJB Call by :testuser2 authenticated? : true / test2 ? : true / test ? : false
EJB Call by :testadmin authenticated? : true / test2 ? : true / test ? : true

到目前为止,这是我测试的全部内容。我不明白为什么部署描述符中声明的角色被JAX-RS服务正确使用,而不是无状态EJB。我需要你的帮助,我并不是真的在每个EJB上使用样板Roles注释。

2 个答案:

答案 0 :(得分:1)

我不知道EJB 3.2Java EE 7规范要求或至少建议兼容实现的容器来支持其他人的安全角色声明。相反,我认为这些文件的含义是,这些声明受到约束 - 至少从“用户空间”代码的角度来看 - 在它们被定义的操作环境中。

关于你的观察:

  • GlassFish是JSR-115 - ( JACC - )兼容的Application Server。根据该规范的§§3.1.1 - 3.1.5,§§4.2 - 4.3,通过web.xmlejb-jar.xml元素或等效注释声明的角色将被“翻译”为{{ 3}} s和WebRoleRefPermission分别由默认的JACC提供程序提供;因此他们是完全不同的。这些以及一些其他Java EE特定的Permission - 至少在理论上 - 可以从“用户空间”代码访问,例如,正如所展示EJBRoleRefPermission(这种方法在GlassFish 4.1上实际上对我有用)。
  • Java EE中的
  • JAX-RS - 即。特别是 Jersey - 位于Servlet基础架构之上。这主要是hereSecurityContext.isUserInRole(String)在此方案中等效的原因。但是,对于不同的假设Servlet独立实现,情况并非如此,并且需要再次定义安全角色两次。

总之,我不认为这种行为违反了规范。这可能是违反直觉的 - 但这是一个不同的故事。

顺便说一句,我完全同意平台安全设施(特别是JACC和JASPIC)的资源严重缺乏令人沮丧;我至少期望在官方教程中包含关于每个的专门章节。如果不是HttpServletRequest.isUserInRole(String) Arjan Tijms's extensive collection of - in depth这个问题,我们很多人仍然会留在黑暗中。人们只能希望新的articles能够帮助改变它 - 更多 - 没有(希望!)重新发明轮子和/或在Servlet Filter上面层层叠来。

答案 1 :(得分:1)

提示让您的web.xml缩短一点。 Servlet不需要以下内容:

<security-role-ref>
    <description>Test2</description>
        <role-name>test2</role-name>
        <role-link>test2</role-link>
</security-role-ref>

如果您的二进制Servlet使用的角色与您的应用程序使用的角色不同,则只应使用此方法。 role-refs已经默认为应用程序角色。

您的测试看起来非常像这个:https://github.com/javaee-samples/javaee7-samples/tree/master/jaspic/ejb-propagation

在GlassFish 4.1(比您使用的版本高一个版本)上,如果我的记忆正确地为我提供了测试,那么该测试就会通过。