原文: https://howtodoinjava.com/resteasy/resteasy-containerrequestfilter-example/

了解如何使用 RESTEasy ContainerRequestFilter创建安全筛选器,该筛选器能够对基于 RESTEasy 的 Web 应用执行认证和授权。

1. RESTEasy ContainerRequestFilterContainerReponseFilter

最近发布了新的 RESTEasy 版本 3.0.2.Final,并使其与 JAX-RS 2.0 兼容。 如果您还记得以前的 JAX-RS 版本没有关于实现过滤器和拦截器的规范。 这就是所有 JAX-RS 实现都有自己独特风格的原因。 RESTEasy 的PreProcessorInterceptorPostProcessorInterceptor现在已弃用。

现在,JAX-RS 在过滤器和拦截器方面有了自己的规范。 您可以阅读 Bill Burke 的帖子中的详细讨论

在 resteasy 中,过滤器在调用resource方法之前和之后运行。 这些过滤器实质上是ContainerRequestFilterContainerReponseFilterContainerRequestFilter在调用 JAX-RS 资源方法之前运行。 ContainerResponseFilter在调用 JAX-RS 资源方法之后运行。 需要注意的是,ContainerRequestFilter有两种风格:匹配前和匹配后。 预先匹配的ContainerRequestFilter@PreMatching注解指定,并且将在 JAX-RS 资源方法与传入的 HTTP 请求匹配之前执行。 在匹配 Java 资源方法之后,执行匹配后的ContainerRequestFilters

过滤器修改请求或响应头时,拦截器处理消息正文。 它们可用于实现特定的内容编码。 它们可用于生成数字签名,或在编组 Java 对象模型之前或之后发布或预处理 Java 对象模型。

2. RESTEasy ContainerRequestFilter示例

在本文中,我将修改 resteasy 认证和授权教程,该教程最初是使用PreProcessorInterceptor在 RESTEasy 2.3.1.GA 中编写的。 我已将其更新为基于 JAX-RS 2.0 规范的 RESTEasy 版本 3.0.2.Final。

2.1 更新 Maven 依赖项

在使用 Maven 时,我已经更新了pom.xml文件,如下所示。 如果您使用的是 ant 或 jar 文件,请相应地更新所需的 jar。

  1. <dependencies>
  2. <!-- core library -->
  3. <dependency>
  4. <groupId>org.jboss.resteasy</groupId>
  5. <artifactId>resteasy-jaxrs</artifactId>
  6. <version>3.0.2.Final</version>
  7. </dependency>
  8. <!-- JAXB support -->
  9. <dependency>
  10. <groupId>org.jboss.resteasy</groupId>
  11. <artifactId>resteasy-jaxb-provider</artifactId>
  12. <version>3.0.2.Final</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.jboss.resteasy</groupId>
  16. <artifactId>jaxrs-api</artifactId>
  17. <version>3.0.2.Final</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>net.sf.scannotation</groupId>
  21. <artifactId>scannotation</artifactId>
  22. <version>1.0.3</version>
  23. </dependency>
  24. </dependencies>

2.2 RESTEasy 安全拦截器

由于 JAX-RS 2.0 具有用于处理前后请求的过滤器,因此我们将使用ContainerRequestFilter接口。 记住PreProcessorInterceptor现在已弃用。

  1. @Provider
  2. public class SecurityInterceptor implements javax.ws.rs.container.ContainerRequestFilter
  3. {
  4. @Override
  5. public void filter(ContainerRequestContext requestContext)
  6. {
  7. //More code...
  8. }
  9. }

现在,首先我们必须访问资源方法以检查安全性约束及其定义的属性。

  1. ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker)
  2. requestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
  3. Method method = methodInvoker.getMethod();

现在我们可以访问资源方法了。 现在,一切将与我们之前所做的相同。 即

  • 检查PermitAll注解(如果存在),则无需进一步检查任何内容
  • 检查DenyAll注解(如果存在),然后返回拒绝访问
  • 检查RolesAllowed注解,然后从注解中获取所需的角色。 从请求中获取授权信息,并根据应用逻辑进行匹配。 如果授权成功,则授予访问权限,否则返回拒绝访问权限。

2.3 RESTEasy SecurityInterceptor源代码

SecurityInterceptor的完整代码如下。

  1. package com.howtodoinjava.demo.rest.security;
  2. import java.io.IOException;
  3. import java.lang.reflect.Method;
  4. import java.util.Arrays;
  5. import java.util.HashSet;
  6. import java.util.List;
  7. import java.util.Set;
  8. import java.util.StringTokenizer;
  9. import javax.annotation.security.DenyAll;
  10. import javax.annotation.security.PermitAll;
  11. import javax.annotation.security.RolesAllowed;
  12. import javax.ws.rs.container.ContainerRequestContext;
  13. import javax.ws.rs.core.MultivaluedMap;
  14. import javax.ws.rs.ext.Provider;
  15. import org.jboss.resteasy.core.Headers;
  16. import org.jboss.resteasy.core.ResourceMethodInvoker;
  17. import org.jboss.resteasy.core.ServerResponse;
  18. import org.jboss.resteasy.util.Base64;
  19. /**
  20. * This interceptor verify the access permissions for a user
  21. * based on username and passowrd provided in request
  22. * */
  23. @Provider
  24. public class SecurityInterceptor implements javax.ws.rs.container.ContainerRequestFilter
  25. {
  26. private static final String AUTHORIZATION_PROPERTY = "Authorization";
  27. private static final String AUTHENTICATION_SCHEME = "Basic";
  28. private static final ServerResponse ACCESS_DENIED = new ServerResponse("Access denied for this resource", 401, new Headers<Object>());;
  29. private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("Nobody can access this resource", 403, new Headers<Object>());;
  30. private static final ServerResponse SERVER_ERROR = new ServerResponse("INTERNAL SERVER ERROR", 500, new Headers<Object>());;
  31. @Override
  32. public void filter(ContainerRequestContext requestContext)
  33. {
  34. ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) requestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
  35. Method method = methodInvoker.getMethod();
  36. //Access allowed for all
  37. if( ! method.isAnnotationPresent(PermitAll.class))
  38. {
  39. //Access denied for all
  40. if(method.isAnnotationPresent(DenyAll.class))
  41. {
  42. requestContext.abortWith(ACCESS_FORBIDDEN);
  43. return;
  44. }
  45. //Get request headers
  46. final MultivaluedMap<String, String> headers = requestContext.getHeaders();
  47. //Fetch authorization header
  48. final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);
  49. //If no authorization information present; block access
  50. if(authorization == null || authorization.isEmpty())
  51. {
  52. requestContext.abortWith(ACCESS_DENIED);
  53. return;
  54. }
  55. //Get encoded username and password
  56. final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
  57. //Decode username and password
  58. String usernameAndPassword = null;
  59. try {
  60. usernameAndPassword = new String(Base64.decode(encodedUserPassword));
  61. } catch (IOException e) {
  62. requestContext.abortWith(SERVER_ERROR);
  63. return;
  64. }
  65. //Split username and password tokens
  66. final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
  67. final String username = tokenizer.nextToken();
  68. final String password = tokenizer.nextToken();
  69. //Verifying Username and password
  70. System.out.println(username);
  71. System.out.println(password);
  72. //Verify user access
  73. if(method.isAnnotationPresent(RolesAllowed.class))
  74. {
  75. RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
  76. Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
  77. //Is user valid?
  78. if( ! isUserAllowed(username, password, rolesSet))
  79. {
  80. requestContext.abortWith(ACCESS_DENIED);
  81. return;
  82. }
  83. }
  84. }
  85. }
  86. private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet)
  87. {
  88. boolean isAllowed = false;
  89. //Step 1\. Fetch password from database and match with password in argument
  90. //If both match then get the defined role for user from database and continue; else return isAllowed [false]
  91. //Access the database and do this part yourself
  92. //String userRole = userMgr.getUserRole(username);
  93. String userRole = "ADMIN";
  94. //Step 2\. Verify user role
  95. if(rolesSet.contains(userRole))
  96. {
  97. isAllowed = true;
  98. }
  99. return isAllowed;
  100. }
  101. }

2.4 RESTEasy 安全过滤器演示

要测试安全代码,请将 Web 应用部署在任何应用服务器(例如 Tomcat)中。 现在,发送以下请求:

  • 不带用户名和密码的 HTTP GET http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
    用户能够成功访问 API。
    RESTEasy `ContainerRequestFilter` - RESTEasy 安全过滤器示例 - 图1

  • 不带用户名和密码的 HTTP PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
    用户无法访问 API。
    RESTEasy `ContainerRequestFilter` - RESTEasy 安全过滤器示例 - 图2

  • 添加基本授权凭据
    RESTEasy `ContainerRequestFilter` - RESTEasy 安全过滤器示例 - 图3

  • 带有用户名和密码的 HTTP PUT http://localhost:8080/RESTEasyEtagDemo/user-service/users/1
    用户能够访问受保护的 API
    RESTEasy `ContainerRequestFilter` - RESTEasy 安全过滤器示例 - 图4

以上就是 resteasy 安全拦截器示例。 如果您有任何疑问或建议,请给我评论。

下载面向 Jboss 的源码

更新:以下是在 tomcat 7 中运行此项目的步骤。

今天,我再次致力于在 tomcat 7 上运行的该项目。要成功运行,我执行了以下步骤:

  • 在 eclipse 中导入项目

  • 在项目根文件夹中运行提示符 > mvn eclipse:eclipse -Dwtpversion=2.0参考

  • 更新方法上的@Produces@Consumes注解

  • 启动 tomcat 服务器并测试应用。 您将获得理想的结果。

下载面向 Tomcat 7 的源码

学习愉快!