如何访问由@RolesAllowed保护的Jersey资源

我们正在通过邮递员rest客户端测试在泽西开发的REST Web服务。 它是一个POST方法,并使用@RolesAllowed注释。 完整注释方法如下:

 @POST @Path("/configuration") @RolesAllowed("admin") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) 

当我使用预期的HTTP正文内容请求这个http://baseurl/configuration时,我得到了403响应(因为它似乎只允许管理员使用它,所以它是预期的)。

我怀疑的是如何通过rest客户端使用指定的角色访问此服务。

因此,您似乎设置了RolesAllowedDynamicFeature ,但您没有进行身份validation以设置用户和角色。 RolesAllowedDynamicFeature执行的操作是查找SecurityContext ,并调用SecurityContext.isUserInRole(<"admin">)以查看SecurityContext的用户是否具有该角色。

我想你不知道如何设置SecurityContext 。 有几种方法。 第一种是通过servlet认证机制。 您可以从Java EE教程中的“ 确保Web应用程序安全”中了解更多信息。

基本上,您需要在服务器上设置安全领域或安全域。 每个服务器都有自己特定的设置方式。 您可以在此处查看示例或如何使用Tomcat完成此操作。

基本上,领域/域包含允许访问Web应用程序的用户。 这些用户具有相关角色。 当servlet容器执行身份validation时,无论是基本身份validation还是表单身份validation,它都会从凭据中查找用户,如果用户经过身份validation,则用户及其角色与请求相关联。 Jersey收集此信息并将其放入SecurityContext以获取请求。

如果这看起来有点复杂,更容易忘记servlet容器身份validation,只需创建一个Jerseyfilter,您可以自己设置SecurityContext 。 你可以在这里看到一个例子。 您可以使用您想要的任何身份validation方案。 重要的部分是使用用户信息设置SecurityContext ,无论您从何处获取,都可能是访问数据存储的服务。

也可以看看:

  • 在泽西岛获得rest服务

UPDATE

以下是使用filter的第二个选项的完整示例。 该测试由Jersey Test Framework运行。 您可以按原样运行测试

 import java.io.IOException; import java.nio.charset.Charset; import java.security.Principal; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Priority; import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Priorities; import javax.ws.rs.WebApplicationException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.ext.Provider; import javax.xml.bind.DatatypeConverter; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.internal.util.Base64; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; import org.glassfish.jersey.test.JerseyTest; import static junit.framework.Assert.*; import org.junit.Test; public class BasicAuthenticationTest extends JerseyTest { @Provider @Priority(Priorities.AUTHENTICATION) public static class BasicAuthFilter implements ContainerRequestFilter { private static final Logger LOGGER = Logger.getLogger(BasicAuthFilter.class.getName()); @Inject private UserStore userStore; @Override public void filter(ContainerRequestContext requestContext) throws IOException { String authentication = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); if (authentication == null) { throw new AuthenticationException("Authentication credentials are required"); } if (!authentication.startsWith("Basic ")) { return; } authentication = authentication.substring("Basic ".length()); String[] values = new String(DatatypeConverter.parseBase64Binary(authentication), Charset.forName("ASCII")).split(":"); if (values.length < 2) { throw new WebApplicationException(400); } String username = values[0]; String password = values[1]; LOGGER.log(Level.INFO, "{0} - {1}", new Object[]{username, password}); User user = userStore.getUser(username); if (user == null) { throw new AuthenticationException("Authentication credentials are required"); } if (!user.password.equals(password)) { throw new AuthenticationException("Authentication credentials are required"); } requestContext.setSecurityContext(new MySecurityContext(user)); } } static class MySecurityContext implements SecurityContext { private final User user; public MySecurityContext(User user) { this.user = user; } @Override public Principal getUserPrincipal() { return new Principal() { @Override public String getName() { return user.username; } }; } @Override public boolean isUserInRole(String role) { return role.equals(user.role); } @Override public boolean isSecure() { return true; } @Override public String getAuthenticationScheme() { return "Basic"; } } static class AuthenticationException extends WebApplicationException { public AuthenticationException(String message) { super(Response .status(Status.UNAUTHORIZED) .header("WWW-Authenticate", "Basic realm=\"" + "Dummy Realm" + "\"") .type("text/plain") .entity(message) .build()); } } class User { public final String username; public final String role; public final String password; public User(String username, String password, String role) { this.username = username; this.password = password; this.role = role; } } class UserStore { public final Map users = new ConcurrentHashMap<>(); public UserStore() { users.put("peeskillet", new User("peeskillet", "secret", "USER")); users.put("stackoverflow", new User("stackoverflow", "superSecret", "ADMIN")); } public User getUser(String username) { return users.get(username); } } private static final String USER_RESPONSE = "Secured User Stuff"; private static final String ADMIN_RESPONSE = "Secured Admin Stuff"; private static final String USER_ADMIN_STUFF = "Secured User Admin Stuff"; @Path("secured") public static class SecuredResource { @GET @Path("userSecured") @RolesAllowed("USER") public String getUser() { return USER_RESPONSE; } @GET @Path("adminSecured") @RolesAllowed("ADMIN") public String getAdmin() { return ADMIN_RESPONSE; } @GET @Path("userAdminSecured") @RolesAllowed({"USER", "ADMIN"}) public String getUserAdmin() { return USER_ADMIN_STUFF; } } @Override public ResourceConfig configure() { return new ResourceConfig(SecuredResource.class) .register(BasicAuthFilter.class) .register(RolesAllowedDynamicFeature.class) .register(new AbstractBinder(){ @Override protected void configure() { bind(new UserStore()).to(UserStore.class); } }); } static String getBasicAuthHeader(String username, String password) { return "Basic " + Base64.encodeAsString(username + ":" + password); } @Test public void should_return_403_with_unauthorized_user() { Response response = target("secured/userSecured") .request() .header(HttpHeaders.AUTHORIZATION, getBasicAuthHeader("stackoverflow", "superSecret")) .get(); assertEquals(403, response.getStatus()); } @Test public void should_return_200_response_with_authorized_user() { Response response = target("secured/userSecured") .request() .header(HttpHeaders.AUTHORIZATION, getBasicAuthHeader("peeskillet", "secret")) .get(); assertEquals(200, response.getStatus()); assertEquals(USER_RESPONSE, response.readEntity(String.class)); } @Test public void should_return_403_with_unauthorized_admin() { Response response = target("secured/adminSecured") .request() .header(HttpHeaders.AUTHORIZATION, getBasicAuthHeader("peeskillet", "secret")) .get(); assertEquals(403, response.getStatus()); } @Test public void should_return_200_response_with_authorized_admin() { Response response = target("secured/adminSecured") .request() .header(HttpHeaders.AUTHORIZATION, getBasicAuthHeader("stackoverflow", "superSecret")) .get(); assertEquals(200, response.getStatus()); assertEquals(ADMIN_RESPONSE, response.readEntity(String.class)); } } 

这是运行测试所需的唯一依赖项

  org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-grizzly2 ${jersey2.version} test