【问题标题】:JAX-RS (Jersey 2) security, @PermitAll and @RolesAllowed not working as expectedJAX-RS (Jersey 2) 安全性、@PermitAll 和 @RolesAllowed 未按预期工作
【发布时间】:2017-09-09 12:07:45
【问题描述】:

我有一个包含三个资源的 REST-API。第一个中的方法称为 PublicResource,任何人都应该可以访问(即匿名访问)。第二个中的方法,称为 SecretResource,应该只可供特定的用户组访问。最后,称为 MixedResource 的第三个资源具有混合设置,其中一些方法受到保护,而另一些则开放供公众访问。

注解@PermitAll 和@RolesAllowed 并没有像我期望的那样工作。尽管 PublicResource 使用 @PermitAll 进行了注释,但在尝试访问它时仍然会被要求授权。 MixedResource 中使用@PermitAll 注释的方法也是如此。所以基本上,我的所有资源都被要求授权,即使我应该有匿名访问权限。

我在 Payara 4.1 上运行,我很困惑,因为我在 WebLogic 12.1.3 上运行的另一个应用程序中进行了非常相似的设置,并且注释按预期工作。我错过了什么或做错了什么?请参阅下面的完整代码。

PublicResource.java:

    import javax.annotation.security.PermitAll;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;

    @Path("public")
    @PermitAll
    public class PublicResource {

        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String itsPublic() {
            return "public";
        }

    }

SecretResource.java:

    import javax.annotation.security.RolesAllowed;
    import javax.ws.rs.GET;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;

    @Path("secret")
    @RolesAllowed({ "SECRET" })
    public class SecretResource {

        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String itsSecret() {
            return "secret";
        }

    }

MixedResource.java:

    import javax.annotation.security.PermitAll;
    import javax.annotation.security.RolesAllowed;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;

    @Path("mixed")
    @PermitAll
    public class MixedResource {

        @GET
        @Path("public")
        @Produces(MediaType.TEXT_PLAIN)
        public String itsPublic() {
            return "public";
        }

        @GET
        @Path("secret")
        @RolesAllowed({ "SECRET" })
        @Produces(MediaType.TEXT_PLAIN)
        public String itsSecret() {
            return "secret";
        }

    }

JAXRSConfiguration.java:

    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;

    @ApplicationPath("resources")
    public class JAXRSConfiguration extends Application {

    }

web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
        <session-config>
            <session-timeout>30</session-timeout>
        </session-config>
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>Basic Authorization</web-resource-name>
                <description/>
                <url-pattern>/resources/*</url-pattern>
            </web-resource-collection>
            <auth-constraint>
                <role-name>SECRET</role-name>
            </auth-constraint>
        </security-constraint>
        <login-config>
            <auth-method>BASIC</auth-method>
            <realm-name>file</realm-name>
        </login-config>
        <error-page>
            <exception-type>java.lang.Throwable</exception-type>
            <location>/error/internal</location>
        </error-page>
        <security-role>
            <role-name>SECRET</role-name>
        </security-role>
    </web-app>

glassfish-web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!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 error-url="">
        <class-loader delegate="true"/>
        <security-role-mapping>
            <role-name>SECRET</role-name>
            <group-name>cia</group-name>
        </security-role-mapping>
        <jsp-config>
            <property name="keepgenerated" value="true">
                <description>Keep a copy of the generated servlet class' java code.</description>
            </property>
        </jsp-config>
    </glassfish-web-app>

beans.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
   bean-discovery-mode="all">
    </beans>

【问题讨论】:

    标签: java rest security jersey jax-rs


    【解决方案1】:

    从表面上看,这不是泽西岛的问题。您配置的唯一真正的安全性是在 servlet 容器级别。您的 Jersey 安全注释都没有任何效果,因为您甚至还没有配置 Jersey 特定的支持。

    先看看你的web.xml配置

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Basic Authorization</web-resource-name>
            <description/>
            <url-pattern>/resources/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>SECRET</role-name>
        </auth-constraint>
    </security-constraint>
    

    第一部分设置每个资源都需要身份验证。所以您认为@PermitAll 不起作用的问题,实际上是由于您将servlet 配置为不允许未经身份验证的任何人通过。

    然后您在 servlet 容器级别设置授权以仅允许具有SECRET 角色的用户。所以你所有的安全配置都是在 web.xml 中配置的。与泽西岛无关。

    要解决此问题,您需要了解的另一件事是身份验证和授权之间的区别,以及谁在什么方面发挥作用(关于 servlet 容器和 Jersey)。

    @PermitAll@RolesAllowed 等只是 Jersey 处理授权的方式。预计已经有一个经过身份验证的用户。 Jersey 如何检查这一点是通过从 servlet 请求中获取主体。如果没有主体,则假定没有经过身份验证的用户,然后将不允许 anyone 通过,即使您有@PermitAll,因为即使@PermitAll 也需要用户已通过身份验证。

    话虽如此,我不知道有没有办法在您的服务器中设置匿名用户。这是获得您想要的行为所需要的。请记住,泽西岛仍然要求有经过身份验证的用户。因此,除非您能以某种方式在容器中设置匿名用户,否则我认为您无法完成这项工作。

    如果您有不同的安全路径和非安全路径,那么您可以在 web.xml 中配置安全路径,其他一切都放手。有了这个,甚至不需要@PermitAll。请记住,使用@PermitAll,它需要经过身份验证的用户。但是如果你只是删除@PermitAll,那么所有的请求都会通过。如果您将安全和非安全之间的路径分开,这将是可能的。

    如果您想更好地控制这一切,您最好实现自己的安全性,而不是使用 servlet 容器。通常我不会推荐这个,但基本身份验证可能是最容易实现的安全协议。你可以查看this example,这里的一切都是使用 Jersey 过滤器完成的。

    最后要注意的是,您甚至还没有为 Jersey 配置授权支持。您仍然需要在应用程序中注册RolesAllowedDynamicFeature。由于您为 JAX-RS 配置使用类路径扫描(空应用程序类),因此您需要从 Feature 注册它,如 this post 中所述

    【讨论】:

    • 感谢您非常彻底的回答。非常感激。是的,将 SecretResource 和 PublicResource 放在不同的路径中是可行的。这是我尝试的第一件事,即具有受保护访问权限的“localhost:8080/resouces/secret/*”和具有匿名/公共访问权限的“localhost:8080/resources/public/*”。是 MixedResource 绊倒了我,让它们都在同一个基本路径中,而且 PermitAll 在我之前的项目中似乎以不同的方式工作。无论如何,谢谢你的指点。
    • 当资源在 web.xml 中不受限制时,@PermitAll 将允许访问该资源。因此,如果您不限制资源,那么您就不需要用户。然后@PermitAll 将允许运行该资源。
    • 因此 web.xml 中的约束是始终需要指定角色之一。而且因为您通常总是需要一个用户来确定角色,所以在这种情况下您总是有一个用户(但您可以创建一个只设置组/角色的安全提供程序)。我不知道是否可以将容器配置为需要用户但不一定需要任何角色。
    猜你喜欢
    • 2019-04-08
    • 2011-09-25
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-18
    相关资源
    最近更新 更多