java 如何访问由@RolesAllowed 保护的 Jersey 资源
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32817210/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to access Jersey resource secured by @RolesAllowed
提问by Tom Sebastian
We were testing a REST webservice developed in jersey through postman rest client. It is a POST method and is annotated with @RolesAllowed
. The full annotation the method is as follows:
我们正在测试通过邮递员休息客户端在 jersey 中开发的 REST 网络服务。它是一个 POST 方法,并用@RolesAllowed
. 该方法的完整注释如下:
@POST
@Path("/configuration")
@RolesAllowed("admin")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
When I requested this http://baseurl/configuration
with the expected HTTP body content, I got 403
response(it is expected since it is allowed only for admin as it seems).
当我http://baseurl/configuration
用预期的 HTTP 正文内容请求这个时,我得到了403
响应(这是预期的,因为它似乎只允许 admin 使用)。
My doubt is how to access this service with the specified role via rest client.
我的疑问是如何通过休息客户端以指定的角色访问此服务。
回答by Paul Samsotha
So it seems like you set up the RolesAllowedDynamicFeature
, but you have no authentication happening to set up the user and roles. What the RolesAllowedDynamicFeature
does is lookup the SecurityContext
, and calls the SecurityContext.isUserInRole(<"admin">)
to see if the user in the SecurityContext
has the role.
因此,您似乎设置了RolesAllowedDynamicFeature
,但是您没有进行身份验证来设置用户和角色。什么RolesAllowedDynamicFeature
做的是查找的SecurityContext
,并调用SecurityContext.isUserInRole(<"admin">)
查看是否在用户SecurityContext
中的角色。
I imagine you don't know how the SecurityContext
is set. There are a couple of ways. The first is through the servlet authentication mechanism. You can see more at Securing Web Applicationsfrom the Java EE tutorial.
我想你不知道它SecurityContext
是如何设置的。有几种方法。第一种是通过servlet 身份验证机制。您可以在来自 Java EE 教程的保护 Web 应用程序中看到更多信息。
Basically you need to set up a security realm or security domain on the server. Every server has it's own specific way of setting it up. You can see an example hereor how it would be done with Tomcat.
基本上,您需要在服务器上设置安全领域或安全域。每个服务器都有自己特定的设置方式。您可以在此处查看示例或如何使用 Tomcat 完成此操作。
Basically the realm/domain contains the users allowed to access the web app. Those users have associated roles. When the servlet container does the authentication, whether it be Basic authentication or Form authentication, it looks up the user from the credentials, and if the user is authenticated, the user and its roles are associated with the request. Jersey gathers this information and puts it into the SecurityContext
for the request.
基本上领域/域包含允许访问网络应用程序的用户。这些用户具有关联的角色。当 servlet 容器进行身份验证时,无论是 Basic 身份验证还是 Form 身份验证,它都会从凭据中查找用户,如果用户通过身份验证,则用户及其角色与请求相关联。Jersey 收集此信息并将其放入SecurityContext
请求中。
If this seems a bit complicated, an easier way to just forget the servlet container authentication and just create a Jersey filter, where you set the SecurityContext
yourself. You can see an example here. You can use whatever authentication scheme you want. The important part is setting the SecurityContext
with the user information, wherever you get it from, maybe a service that accesses a data store.
如果这看起来有点复杂,这是一种更简单的方法,可以忘记 servlet 容器身份验证并创建一个 Jersey 过滤器,您可以在其中自行设置SecurityContext
。您可以在此处查看示例。您可以使用任何您想要的身份验证方案。重要的部分是设置SecurityContext
用户信息,无论您从何处获取它,可能是访问数据存储的服务。
See Also:
也可以看看:
UPDATE
更新
Here is a complete example of the second option using the filter. The test is run by Jersey Test Framework. You can run the test as is
这是使用过滤器的第二个选项的完整示例。该测试由Jersey 测试框架运行。您可以按原样运行测试
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<String, User> 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));
}
}
Here is the only dependency needed to run the test
这是运行测试所需的唯一依赖项
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>