2

我正在尝试将自己的身份验证和授权添加到 Java EE REST 应用程序。我已经设法获得了带有 JAX-RS 的工作版本SecurityContextContainerRequestFilter实现(使用 JWT)和@RolesAllowed端点方法的注释。但是我需要 EJB,而且它们根本不使用 JAX-RS 的 SecurityContext(EJBAccessException无论用户角色如何,我都会得到),所以我需要另一个解决方案。

在 EJB 中是否有类似的东西SecurityContext可以实现?或者我应该使用像 Shiro 这样的图书馆吗?我想从应用程序本身管理用户,因此容器或 LDAP 提供的用户管理不是一个选项。我正在使用 JPA 对用户进行身份验证和授权。

所以,主要问题是:

如何基于 JAX-RS 过滤器在 EJB 中实现自己的身份验证和基于角色的授权机制(使用 @RolesAllowed 注释)?我如何告诉 EJB 请求与具有这些角色的具体经过身份验证的用户相关?

还有一件事——我宁愿避免使用特定于供应商的解决方案,但如果必须,我会选择 JBoss/Wildfly。

4

4 回答 4

2

您当前的解决方案是否正确设置了Principal对象?它是 Java EE 安全性的核心,包括 EJB。

通常,您需要一个支持 JPA 和自定义身份验证方法的 auth + IDM解决方案;PicketLink可能是您的选择。不幸的是,PicketLink 现在据说被KeyCloak取代,我个人认为这是一个有争议的决定。KeyCloak 不提供应用程序内 IDM - 它是一项重要的功能,而它正是您正在寻找的。

JSR 375:Java™ EE 安全 API是一种新兴规范,它将以标准的、供应商中立的方式解决上述所有问题。Soteria是一个 JSR 375 RI。目前,它仅支持只读身份存储。

于 2016-03-26T19:20:51.367 回答
1

我会说您将在 REST Api 上使用 HTTP 身份验证标头,这将通过容器的配置进行验证。仅仅因为还没有 Java EE 供应商中立规范,所以会有特定供应商的实现。验证用户后,将Principal创建 a 并且所有 EJB @RolesAllowed 注释都将起作用。

我将DukesForest 移植到 Wildfly,因此您可以看到它的示例。查看 dukes-payment 的其余服务,注意 web.xml 和 jboss-web.xml。另外,查看数据库配置的实体项目:

基本上,web.xml 将定义安全约束:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Secure payment service</web-resource-name>
        <description/>
        <url-pattern>/*</url-pattern>
        <http-method-omission>GET</http-method-omission>
    </web-resource-collection>
    <auth-constraint>
        <role-name>USERS</role-name>
    </auth-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>
</login-config>
<security-role>
    <role-name>USERS</role-name>
</security-role>

而且 Wildfly 需要security-domain添加一个来指定如何查询数据库:

<security-domain name="dukes-forest" cache-type="default">
    <authentication>
        <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
            <module-option name="dsJndiName" value="java:jboss/ForestXADS"/>
            <module-option name="rolesQuery" value="select NAME as 'ROLES', 'Roles' as 'ROLEGROUP' from forest.GROUPS g inner join forest.PERSON_GROUPS pg on g.ID = pg.GROUPS_ID join forest.PERSON p on p.EMAIL = pg.EMAIL where p.EMAIL = ?"/>
            <module-option name="hashAlgorithm" value="MD5"/>
            <module-option name="hashEncoding" value="HEX"/>
            <module-option name="principalsQuery" value="select PASSWORD from forest.PERSON where EMAIL=?"/>
        </login-module>
    </authentication>
    <authorization>
        <policy-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
            <module-option name="dsJndiName" value="java:jboss/ForestXADS"/>
            <module-option name="rolesQuery" value="select NAME as 'ROLE', 'ROLES' as 'ROLEGROUP' from forest.GROUPS g inner join forest.PERSON_GROUPS pg on g.ID = pg.GROUPS_ID join forest.PERSON p on p.EMAIL = pg.EMAIL where p.EMAIL = ?"/>
            <module-option name="hashAlgorithm" value="MD5"/>
            <module-option name="hashEncoding" value="HEX"/>
            <module-option name="principalsQuery" value="select PASSWORD from forest.PERSON where EMAIL=?"/>
        </policy-module>
    </authorization>
</security-domain>

这是基本思想。

PS> 还有一个Java Security Quickstart Archetype在web框架中实现安全,根据上面的例子添加Http Basic Authentication应该很容易。

于 2016-03-27T16:02:43.000 回答
1

我已经为此工作了一段时间,最终得到了我自己的解决方案。它是在JAX-RS接口级别实现的(如Pradeep Pati所建议的)——因此,如果您需要通过 访问您的 bean EJB,它将无法工作。

所以,正如我在这里找到的,ContainerRequestFilter可以访问资源方法(或类)注释,所以我需要做的就是:

1.实现我自己的@RolesAllowed注解:

@Inherited
@Target( {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RolesAllowed {
    String[] value();
}

2.ContainerRequestFilter使用自定义身份验证和授权实现:

@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext context) throws IOException {
        // here we have access to headers:
        String authorizationHeader = context.getHeaderString("Authorization");

        // and, thanks to injected resourceInfo, to annotations:
        RolesAllowed annotation = resourceInfo
                .getResourceClass() // or getResourceMethod(), I've used both
                    .getAnnotation(RolesAllowed.class);
        // and, finally, to the roles (after a null-check)
        String[] roles = annotation.value();

        // then you can authenticate and authorize everything on your own using any method (I’ve used Basic Auth and JWT)
        // and, if something fails, you can abort the request:
        if (!isAuthenticated) {
            context.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        } else if (!isAuthorized) {
            context.abortWith(Response.status(Response.Status.FORBIDDEN).build());
        }           
    }
    ...
}

但是,我也评估了Avishai的解决方案(使用PicketLink)。虽然这有点难以实现,有时可能很复杂(例如,基本的 JPA 场景需要大约 7-8 个 JPA 实体),但如果您需要具有很多选项的可靠、可扩展的安全系统(如 LDAP 或JPA,甚至两者同时使用)或使用各种身份验证选项(例如同时使用 Basic 和 JWT 身份验证,但具有不同的标头)。这个话题可能有数百个优点和/或缺点,所以这不是一个容易的选择。

有趣的是,PicketLink 使用它自己的@org.picketlink.authorization.annotations.RolesAllowed注释而不是javax.annotation一个。尽管如此,它应该可以很好地处理EJB调用,因为它使用EJB拦截器而不是JAX-RS过滤器来检查角色。

但对我来说,这似乎有点过头了,所以我想出了自己的、不那么复杂(但有效)的解决方案。

于 2016-04-10T20:30:20.260 回答
0

我在 apache shiro 上构建了一个库,以支持 java ee 中的身份验证和授权

https://github.com/panchitoboy/shiro-jwt

您可以在 test 文件夹中看到一个示例。

问候

于 2016-09-07T20:14:35.570 回答