【问题标题】:Stateless EJB isn't aware of roles declared in web.xml无状态 EJB 不知道 web.xml 中声明的角色
【发布时间】:2015-06-17 21:57:35
【问题描述】:

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

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

我将向您展示我正在使用的文件,然后是我迄今为止测试过的不同调用输出的结果。

TL/DR:很抱歉这篇文章太长了。请转到第二部分/测试 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 我的 JAX-RS 服务中还有 2 个 URI .一种是直接给用户信息和角色,另一种是使用 EJB 来检索相同的信息。 如果用户登录,两者都应该返回完全相同的输出。

第二部分:测试

现在,使用网络服务测试器(Mac 上的 Paw,基于 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 中声明的角色不会传播到同一个WAR 项目中的EJB。

测试 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 接口登录并使用带有CORBA 的EJB。除了测试之外,我不打算使用它。

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

测试 1 : 用 @DeclareRoles 注释测试远程 EJB

EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false

测试 2:测试远程 EJB 并取消注释 @DeclareRoles({"authenticated","test","test2"})

EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false
EJB Call by :ANONYMOUS authenticated? : false / test2 ? : false / test ? : false

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

测试 3:测试远程 EJB,添加了 @DeclareRoles@RolesAllowed({"authenticated"})

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

这是我迄今为止测试的所有内容。我不明白为什么 JAX-RS 服务正确使用了部署描述符中声明的角色,但无状态 EJB 却没有。在这方面我需要你的帮助,我不太愿意在每个 EJB 上添加样板角色注释。

【问题讨论】:

  • 嗨@mrik974,您找到任何解决角色传播和匿名问题的方法了吗?
  • 不,它仍然让我很烦。我必须继续前进,但如果有人找到解决方案,我将非常乐意尝试。
  • 这太令人沮丧了,根本没有任何帮助。我在网上找到的所有此类问题都没有答案:(
  • 大家好,你们有什么解决办法吗? GlassFish 4.1 也有同样的问题。我什至尝试添加 glassfish-ejb-jar.xml,但它也没有帮助。

标签: glassfish ejb ejb-3.1 glassfish-4


【解决方案1】:

我不知道EJB 3.2Java EE 7 规范强制或至少推荐兼容实现的容器来支持其他人的安全角色声明。相反,在我看来,这些文档暗示的是,这些声明在它们定义的操作环境中受到约束——至少从“用户空间”代码的角度来看。

关于你的观察:

  • GlassFish 是与JSR-115- (JACC-) 兼容的应用服务器。根据该规范的§§ 3.1.1 - 3.1.5、§§ 4.2 - 4.3,通过web.xmlejb-jar.xml 元素或等效注释声明的角色将被“翻译”成WebRoleRefPermissions 和@ 987654325@s,分别由默认的 JACC 提供者提供;因此它们是不同的。这些和其他一些特定于 Java EE 的Permissions——至少在理论上——可以从“用户空间”代码访问,例如正如 here 所展示的那样(这种方法实际上仍然适用于 GlassFish 4.1)。
  • JAX-RS 在 Java EE 中——即Jersey 特别是——位于 Servlet 基础架构之上。这主要是SecurityContext.isUserInRole(String)HttpServletRequest.isUserInRole(String) 在这种情况下等效的原因。然而,对于另一个假设的独立于 Servlet 的实现,情况并非如此,并且需要再次定义两次安全角色。

总之,我不认为这种行为违反规范。这可能违反直觉——但那是另一回事。

顺便说一句,我完全同意平台的安全设施资源严重缺乏,尤其是 JACC 和 JASPIC,这令人沮丧;我至少希望官方教程中包含一个专门的章节。如果不是 Arjan Tijms's extensive collection of in-depth articles 在这个问题上,我们中的许多人仍然会被蒙在鼓里。人们只能希望新的JSR-375 将有助于改变这一点——甚至更多——而不需要(希望!)重新发明轮子和/或将所有东西分层放在 Servlet Filters 上。

【讨论】:

  • 你完全正确。但正如你所说,这真的是违反直觉的......感谢您提供 Arjan Tijm 博客上的链接!
【解决方案2】:

让您的 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(比您使用的版本高一个版本)上,如果我没记错的话,测试通过了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    • 2018-02-10
    • 1970-01-01
    • 2015-03-27
    • 2012-07-04
    • 1970-01-01
    相关资源
    最近更新 更多