如何访问由@RolesAllowed保护的Jersey资源 [英] How to access Jersey resource secured by @RolesAllowed

查看:119
本文介绍了如何访问由@RolesAllowed保护的Jersey资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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:

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

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

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).

我怀疑如何通过rest client以指定角色访问此服务。

My doubt is how to access this service with the specified role via rest client.

推荐答案

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

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.

我想你不知道如何设置 SecurityContext 。有几种方法。第一种是通过servlet认证机制。您可以在保护Web应用程序中查看更多信息。 Java EE教程。

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 Applications from the Java EE tutorial.

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

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 here or how it would be done with Tomcat.

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

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容器身份验证的方法,只需创建一个Jersey过滤器,您可以自己设置 SecurityContext 。您可以在此处查看示例。您可以使用您想要的任何身份验证方案。重要的是使用用户信息设置 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.

另见:

  • securing rest services in Jersey

以下是使用过滤器的第二个选项的完整示例。该测试由 Jersey测试框架运行。您可以按原样运行测试

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

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>

这篇关于如何访问由@RolesAllowed保护的Jersey资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆