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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 20:49:19  来源:igfitidea点击:

How to access Jersey resource secured by @RolesAllowed

javajerseyjersey-2.0postman

提问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/configurationwith the expected HTTP body content, I got 403response(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 RolesAllowedDynamicFeaturedoes is lookup the SecurityContext, and calls the SecurityContext.isUserInRole(<"admin">)to see if the user in the SecurityContexthas the role.

因此,您似乎设置了RolesAllowedDynamicFeature,但是您没有进行身份验证来设置用户和角色。什么RolesAllowedDynamicFeature做的是查找的SecurityContext,并调用SecurityContext.isUserInRole(<"admin">)查看是否在用户SecurityContext中的角色。

I imagine you don't know how the SecurityContextis 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 SecurityContextfor 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 SecurityContextyourself. You can see an example here. You can use whatever authentication scheme you want. The important part is setting the SecurityContextwith 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>