Как получить SecurityContext в приложении Quarkus?

#java #security #jax-rs #quarkus

Вопрос:

У меня есть приложение Quarkus, в котором я реализовал ContainerRequestFilter интерфейс для сохранения заголовка из входящих запросов:

 @PreMatching
public class SecurityFilter implements ContainerRequestFilter {
   private static final String HEADER_EMAIL = "HD-Email";

   @Override
   public void filter(ContainerRequestContext requestContext) throws IOException {
       String email = requestContext.getHeaders().getFirst(HEADER_EMAIL);

       if (email == null) {
           throw new AuthenticationFailedException("Email header is required");
       }

       requestContext.setSecurityContext(new SecurityContext() {
           @Override
           public Principal getUserPrincipal() {
               return () -> email;
           }

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

           @Override
           public boolean isSecure() {
               return false;
           }

           @Override
           public String getAuthenticationScheme() {
               return null;
           }
       });
   }
}
 

В классе с аннотацией ApplicationScoped я ввел контекст следующим образом:

 @ApplicationScoped
public class ProjectService {
    @Context
    SecurityContext context;
    ...
}
 

Проблема в том, что context атрибут на самом деле никогда не вводится, как это всегда null бывает .

Что я делаю не так? Что я должен сделать, чтобы иметь возможность извлекать SecurityContext весь код приложения?

Ответ №1:

Мне нравится абстрагировать эту проблему, чтобы бизнес-логика не зависела от конструкций, специфичных для JAX-RS. Итак, я создаю класс для описания, скажем User , моего пользователя и другого интерфейса AuthenticationContext , который содержит текущего пользователя и любую другую необходимую мне информацию , связанную с аутентификацией, например:

 public interface AuthenticationContext {
    User getCurrentUser();
}
 

Я создаю RequestScoped реализацию этого класса, которая также имеет соответствующий сеттер(ы):

 @RequestScoped
public class AuthenticationContextImpl implements AuthenticationContext {
    private User user;

    @Override
    public User getCurrentUser() {
        return user;
    }

    public void setCurrentUser(User user) {
        this.user = user;
    }
}
 

Теперь я вставляю этот компонент и JAX-RS SecurityContext в фильтр, который знает, как создать User и установить его в мое конкретное приложение AuthenticationContext :

 @PreMatching
public class SecurityFilter implements ContainerRequestFilter {

    @Inject AuthenticationContextImpl authCtx; // Injecting the implementation,
                                               // not the interface!!!

    @Context SecurityContext securityCtx;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        User user = ...// translate the securityCtx into a User
        authCtx.setCurrentUser(user);
    }
}
 

А затем любой бизнес-компонент, которому нужны пользовательские данные, вводит среду, нейтральную для конкретного приложения AuthenticationContext .

Комментарии:

1. Спасибо! Это действительно помогло!

2. Это просто выбрасывает аутентификациюконтекст равен нулю для меня, я ввожу AuthenticationContextImpl аргумент метода

Ответ №2:

@Context может использоваться только в классах JAX-RS, т. е. классах с аннотациями @Path .

В вашем случае ProjectService это компонент CDI, а не класс JAX-RS.

Канонический способ сделать то, что вы хотите,-это ввести SecurityContext в ресурс JAX-RS, а затем передать его в качестве параметра метода вашему ProjectService

Комментарии:

1. Я понимаю. Вы не знаете, есть ли какой-нибудь другой способ сделать это? Если бы я делал это через каждый контроллер (ресурс JAX-RS), это было бы довольно громоздко. Я также пытался ввести SecurityIdentity объект, но он также не работает (я полагаю, по той же причине, о которой вы упомянули). Спасибо

2. Это единственный стандартный способ сделать это