EJB:自定义身份验证和授权

时间:2016-03-26 16:53:48

标签: java rest java-ee ejb java-security

我正在尝试将自己的身份验证和授权添加到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 个答案:

答案 0 :(得分:2)

您当前的解决方案是否正确设置了min_sphere = {(x_i,r_i) : from POI_min}, spheres_to_cover = {(x_i,r_i) : from POI_max} A = {} while not is_empty(spheres_to_cover) power_set_score = struct // holds score, k PS <- costruct power set of sphere_to_cover for i = 1:number_of_elements(PS) k = PS[i] if intersection(k) \ min_sphere is not empty power_set_score[i].score = |k| else power_set_score[i].score = 0 end if power_set_score[i].k = k end for sort(power_set_score) // sort by score, biggest first A <- add arbitrary point in (intersection(power_set_score[1].k) \ min_sphere) spheres_to_cover = spheres_to_cover \ power_set_score[1].k end while 个对象?它是Java EE安全的核心,包括EJB。

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

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

答案 1 :(得分:1)

我会说你会在REST Api上使用HTTP身份验证标头,这将由Container的配置验证。将会有一个特定于供应商的具体实现,因为还没有Java EE供应商中立规范。验证用户后,将创建Principal并且所有EJB @RolesAllowed注释都将起作用。

我已移植DukesForest to Wildfly,因此您可以看到它的实例。查看dukes-payment for rest service,请注意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&GT;还有一个Java Security Quickstart Archetype在Web框架中实现安全性,并且应该很容易根据上面的示例添加Http Basic Authentication。

答案 2 :(得分:1)

我一直在研究这个问题并最终得到了我自己的解决方案。它是在JAX-RS接口级别实施的(由Pradeep Pati建议) - 因此,如果您需要通过EJB访问您的bean,它将无效。

因此,正如我发现hereContainerRequestFilter可以访问资源方法(或类)注释,所以我需要做的就是:

<强> 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 auth,但具有不同的头文件)。关于这个话题可能有数百个优点和/或缺点,所以这不是一个简单的选择。

有趣的是,PicketLink使用它自己的@org.picketlink.authorization.annotations.RolesAllowed注释而不是javax.annotation注释。不过,它应该适用于EJB次调用,因为它使用EJB拦截器而不是JAX-RS过滤器来检查角色。

但对我而言,这似乎是一种矫枉过正,所以我提出了自己的,不那么复杂(但有效)的解决方案。

答案 3 :(得分:0)

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

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

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

此致