JAX-RS Injecting CDI Event в классе, который реализует ContainerRequestFilter

Я пытаюсь создать JAX-RS Rest API с Джерси. Я слежу за ответом, получившим наибольшее количество голосов в этой теме: Лучшая практика для аутентификации на основе токенов REST с JAX-RS и Джерси

Добрался до части Идентификация текущего пользователя. Пытаюсь использовать CDI.

Вот мой основной класс приложения:

public class Main {
    // Base URI the Grizzly HTTP server will listen on
    public static final String BASE_URI = "http://localhost:8080/myapp/";

    /**
     * Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
     * @return Grizzly HTTP server.
     */
    public static HttpServer startServer() {
        // create a resource config that scans for JAX-RS resources and providers
        // in appServer package
        final ResourceConfig rc = new ResourceConfig().packages("appServer");
        rc.register(new CORSFilter());
        rc.register(new AuthenticationFilter());
        // create and start a new instance of grizzly http server
        // exposing the Jersey application at BASE_URI
        return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc, false);
    }

    /**
     * Main method.
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        final Weld weld = new Weld();
        weld.initialize();

        final HttpServer server = startServer();
        server.start();
        new SessionUtil().buildSession(args);
        System.out.println(String.format("Jersey app started with WADL available at "
                + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
        System.in.read();
        server.stop();
        weld.shutdown();
    }
}

И соответствующий класс фильтра:

import appServer.AuthenticatedUser;
import appServer.Secured;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import javax.annotation.Priority;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Default;
import javax.inject.Inject;
import javax.ws.rs.Priorities;
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.ext.Provider;
import java.io.IOException;

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

    private static final String REALM = "myRealm";
    private static final String AUTHENTICATION_SCHEME = "Bearer";

    public AuthenticationFilter() {
        super();
    }


    @Inject
    @AuthenticatedUser
    Event<String> userAuthenticatedEvent;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // Get the Authorization header from the request
        String authorizationHeader =
                requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            abortWithUnauthorized(requestContext);
            return;
        }

        // Extract the token from the Authorization header
        String token = authorizationHeader
                .substring(AUTHENTICATION_SCHEME.length()).trim();

        try {

            // Validate the token
            validateToken(token);

            // if successful, fire event with token
            userAuthenticatedEvent.fire(token);

        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }

    private boolean isTokenBasedAuthentication(String authorizationHeader) {

        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // The authentication scheme comparison must be case-insensitive
        return authorizationHeader != null && authorizationHeader.toLowerCase()
                .startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
    }

    private void abortWithUnauthorized(ContainerRequestContext requestContext) {

        // Abort the filter chain with a 401 status code response
        // The WWW-Authenticate header is sent along with the response
        requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED)
                        .header(HttpHeaders.WWW_AUTHENTICATE,
                                AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
                        .build());
    }

    private void validateToken(String token) throws Exception {
        // Check if the token was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
}

Когда я запускаю приложение, оно вылетает с такой ошибкой:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=Event,parent=AuthenticationFilter,qualifiers = {@javax.enterprise.inject.Default(),@appServer.AuthenticatedUser()},position=-1,optional=false,self=false,unqualified=null,997918120)

Я столкнулся с этим вопросом: Как использовать события CDI в Java Jersey?, но ответа не было. Я пробовал другие решения, опубликованные для аналогичных проблем, но ни одно из них не сработало.

Итак, здесь явно какая-то проблема с инъекцией:

@AuthenticatedUser
@Inject
Event<String> userAuthenticatedEvent;

Или, может быть, я неправильно регистрирую фильтр. Какие-либо предложения ?

Разве фильтром не нужно управлять CDI? Вы пробовали добавить @ApplicationScoped для фильтрации и зарегистрировать его как класс вместо экземпляра?

Paul Samsotha 27.06.2018 06:54

Я не знаю, как сделать это правильно в Grizzly, но когда вы создаете экземпляр фильтра через new AuthenticationFilter(), вы определенно обходите CDI. Таким образом, поищите какой-нибудь модуль grizzly-cdi (он есть на большинстве серверов), который позволяет автоматически обнаруживать фильтры. Если ничего не помогает, позвольте сварочному контейнеру инициировать фильтр аутентификации, извлекая WeldContainer из инициализации (WeldContainer container = Weld.initialize()), а затем создав его экземпляр с помощью AuthenticationFilter filter = container.select(AuthenticationFilter.class).get(). (Но, пожалуйста: только в крайнем случае ;-))

mtj 27.06.2018 07:35

Если вы аннотируете фильтр с помощью @Provider, среда выполнения grizzly также должна иметь возможность автоматически обнаруживать его.

maress 28.06.2018 13:36
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
3
431
0

Другие вопросы по теме