java - JAX-RS 安全性 - 主体不持久

标签 java jakarta-ee jax-rs wildfly resteasy

首先是代码:

应用设置类:

    @ApplicationPath("/rest")
    public class ApplicationConfig extends Application {

    }

JAX-RS 资源类:

    @Path("/test")
    @RequestScoped
    public class TestWS {
        @POST
        @Path("/login")
        @Produces(MediaType.TEXT_PLAIN)
        public Response login(@Context HttpServletRequest req){
            req.getSession().setAttribute("test","test");
            System.out.println(req.getSession().getId());
            if(req.getUserPrincipal() == null){
                String authHeader = req.getHeader(HttpHeaders.AUTHORIZATION);
                if(authHeader != null && !authHeader.isEmpty()){
                    String base64Credentials = authHeader.substring("Basic".length()).trim();
                    String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                            Charset.forName("UTF-8"));
                    final String[] values = credentials.split(":",2);
                    try {
                        req.login(values[0], values[1]);
                        System.out.println(req.getUserPrincipal().toString());
                    }
                    catch(ServletException e){
                        e.printStackTrace();
                        return Response.status(Response.Status.UNAUTHORIZED).build();
                    }
                }
            }else{
                System.out.println(req.getUserPrincipal());
                System.out.println(req.isUserInRole("User"));
                req.getServletContext().log("Skipped logged because already logged in!");
            }

            req.getServletContext().log("Authentication Demo: successfully retrieved User Profile!");
            return Response.ok().build();
        }

        @Path("/ping")
        @Produces(MediaType.APPLICATION_JSON)
        @GET
        public String ping(@Context HttpServletRequest req){
            Object test = req.getSession().getAttribute("test");
            System.out.println(req.getSession().getId());
            System.out.println(test);
            System.out.println(req.getUserPrincipal());
            System.out.println(req.isUserInRole("User"));
            return "{\"status\":\"ok\"}";
        }
    }

web.xml:

    <?xml version="1.0" encoding="UTF-8" ?>
    <web-app>
        <context-param>
            <param-name>resteasy.role.based.security</param-name>
            <param-value>true</param-value>
        </context-param>

        <security-constraint>
            <web-resource-collection>
                <web-resource-name>rest</web-resource-name>
                <url-pattern>/rest/*</url-pattern>
                <http-method>GET</http-method>
                <http-method>POST</http-method>
                <http-method>PUT</http-method>
                <http-method>DELETE</http-method>
            </web-resource-collection>
            <user-data-constraint>
                <transport-guarantee>NONE</transport-guarantee>
            </user-data-constraint>
        </security-constraint>

        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>

        <session-config>
            <session-timeout>30</session-timeout>
        </session-config>

        <login-config>
            <auth-method>BASIC</auth-method>
            <realm-name>PBKDF2DatabaseDomain</realm-name>
        </login-config>
    </web-app>

jboss-web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
        <context-root>/</context-root>
        <security-domain>PBKDF2DatabaseDomain</security-domain>
    </jboss-web>

standalone.xml 中的安全设置

    <subsystem xmlns="urn:jboss:domain:security:1.2">
        <security-domains>
            <security-domain name="PBKDF2DatabaseDomain" cache-type="default">
                <authentication>
                    <login-module code="de.rtner.security.auth.spi.SaltedDatabaseServerLoginModule" flag="required" module="de.rtner.PBKDF2">
                        <module-option name="dsJndiName" value="java:jboss/datasources/developmentDS"/>
                        <module-option name="principalsQuery" value="SELECT password FROM ur.user WHERE username=?"/>
                        <module-option name="rolesQuery" value="select distinct r.NAME, 'Roles' from ur.user_roles ur left join ur.ct_role r on ur.ROLE_ID = r.ID left join ur.user u on ur.USER_ID = u.ID where u.username =?"/>
                    </login-module>
                </authentication>
            </security-domain>
...

现在我的问题是,在请求中使用带有 BASIC auth header 的/login 方法后,我已经获得了用户主体,因此它打印出来:

由于已经登录而跳过登录!

如果我从请求中删除 BASIC auth header 并再次调用登录,我仍然会得到相同的打印输出 - 所以我已经在请求中拥有用户主体 - 无需 req.login。

但是如果我调用 ping 方法,即使 session id 相同且 session 属性已设置,用户主体也为 null。我究竟做错了什么?我希望用户主体能够像在/login 上一样在/ping 上持续存在。

我正在使用 Wildfly 10(RESTeasy jax-rs 实现)

我的问题类似于: JBOSS AS7 jax-rs jaas and annotations

但是修复 - 在登录方法中设置 session 属性对我不起作用。

最佳答案

一些提示和评论:

  • 由于 REST 是无状态的,我将在客户端管理 session ,而不是在服务器端保留客户端状态。
  • 可以通过为您希望仅限于经过身份验证的用户(例如/login 和/ping)的每个请求/服务发回 BASIC 身份验证 header 来实现无状态身份验证
  • 当用户未经过良好的身份验证和识别时,/login 服务可能会抛出 WebApplicationException,以便您的客户端应用程序对其进行管理并向最终用户显示身份验证错误消息。 然后,您可以在/ping 方法上方使用 @RolesAllowed('something') 注释和 JAAS 来限制对具有 'something' 角色的经过身份验证的用户的访问。因此,您不再需要编写身份验证或授权代码,并可以从应用中的授权层中受益。

所有这一切都可以通过 Java EE 7 在 Wildfly 上实现,无需额外的库。

关于java - JAX-RS 安全性 - 主体不持久,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33866597/

相关文章:

java - Android:openFileOutput 抛出 NullPointerException

java - 在 Spring Integration 中结合几个 channel

java - 用Java开发后端服务器

java - JAX-RS : Model and Best practices

java - 如何在 ContainerRequestFilter 中检索请求的匹配资源

java - oracle.sql.BLOB.DURATION_CALL 与 oracle.sql.BLOB.DURATION_SESSION 之间的区别

java - 加密函数每次返回不同的输出

jakarta-ee - Jersey 和 Filter 异常处理

java - 如何在 jsf 2.0 中获取 html 元素的 jsf 前缀

rest - 使用拦截器和注入(inject)在 JAX-RS 中进行身份验证/授权