#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. Это единственный стандартный способ сделать это