Лучший способ обмена данными между фильтрами запросов JAX-RS - PullRequest
0 голосов
/ 28 июня 2018

Я хочу проверить пользователя, делающего запрос. Для этого у меня есть два фильтра: AuthenticationFilter и AuthorizationFilter. AuthenticationFilter извлекает токен из запроса и находит пользователя из базы данных. AuthorizationFilter проверяет, имеет ли этот пользователь (полученный предыдущим фильтром) необходимые разрешения. У меня есть два возможных решения, и я хотел бы знать плюсы и минусы каждого из них, и какие я должен использовать. Мне также нужно было бы получить доступ к пользователю в реальной бизнес-логике. Мой код выглядит следующим образом:

AuthenticationFilter:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    // option 1.1
    @Inject
    @AuthenticatedUser
    private Event<User> authenticatedUserEvent;

    // option 1.2
    @Inject
    @AuthenticatedUser
    private User authenticatedUser;

    public void filter(ContainerRequestContext requestContext) throws IOException {
        String token = getToken(requestContext);
        User user = getUser(token)
        if (user == null) {
            requestContext.abortWith(...);
        } else {
            // option 1.1
            authenticatedUserEvent.fire(User);

            // option 1.2
            authenticatedUser.setData(user);

            // option 2
            requestContext.setProperty("authenticatedUser", user);
        }
    }
}

AuthorizationFilter:

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    // option 1
    @Inject
    @AuthenticatedUser
    private User authenticatedUser;

    public void filter(ContainerRequestContext requestContext) throws IOException {

        // option 2
        User authenticatedUser = (User) requestContext.getProperty("authenticatedUser")

        boolean allowed = verifyRoles(user, resourceInfo.getResourceClass(), resourceInfo.getResourceMethod());
        if (!allowed) {
            requestContext.abortWith(...);
        }
    }
}

AuthenticatedUserProducer (только для варианта 1):

@RequestScoped
public class AuthenticatedUserProducer {

    @Produces
    @RequestScoped
    @AuthenticatedUser
    private User authenticatedUser;

    public void handleAuthenticationEvent(@Observes @AuthenticatedUser User user) {
        this.authenticatedUser = user
    }
}

Для варианта 1 необходимо пометить фильтры с помощью @RequestScoped? По умолчанию фильтры ограничены областью применения, поэтому безопасна ли инъекция? То есть событие, инициируемое фильтром 1 в цепочке запросов 1, не будет в конечном итоге введено в фильтр 2 цепочки 2, если обрабатывается более одного запроса одновременно? И то же самое при внедрении пользователя в класс ресурсов, где работает настоящая бизнес-логика?

Для варианта 2 у меня больше нет доступа к ContainerRequestContext, но я могу получить доступ к объекту, введя HttpServletRequest. Но эта опция кажется мне «менее чистой», потому что мне нужно привести сохраненные объекты к Пользователю, прежде чем использовать их, когда с помощью инъекционного подхода я могу использовать объекты напрямую.

Я уже проверил эти вопросы, что я ищу, чтобы определить, какой вариант является лучшим:

В настоящее время я использую WildFly 11 с его реализацией по умолчанию JAX-RS (Resteasy) и CDI (Weld).

1 Ответ

0 голосов
/ 28 июня 2018

После выполнения аутентификации / авторизации вы можете использовать SecurityContext, который является частью JAX-RS API. Посмотрите пример того, как использовать его в вашем фильтре аутентификации:

final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {

        @Override
        public Principal getUserPrincipal() {
            // Return a Principal instance according to your needs
            return () -> username;
        }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }

    @Override
    public boolean isSecure() {
        return currentSecurityContext.isSecure();
    }

    @Override
    public String getAuthenticationScheme() {
        // Return the authentication scheme used by your application
        return SecurityContext.BASIC_AUTH;
    }
});

Затем добавьте SecurityContext в ваш ресурс JAX-RS и провайдеров с аннотацией @Context:

@Context
SecurityContext securityContext;

Затем вы можете получить экземпляр Principal, который был установлен в SecurityContext:

Principal principal = securityContext.getUserPrincipal();
...