Ответ 1
Если вы выполняете аутентификацию HTTP-запроса в верхней части JSF, то фильтр-сервлет действительно лучший подход. JSF является "просто" структурой MVC, и ничто в API JSF не было указано для фильтрации входящих HTTP-запросов для проверки подлинности пользователя. В обычных запросах GET управляемый JSF bean обычно создается только тогда, когда HTTP-ответ будет создан и отправлен, или, возможно, уже был отправлен. Это не контролируется внутри управляемого bean. Если ответ уже сделан, вы больше не сможете его изменять (перенаправить). Аутентификация и изменение запроса/ответа действительно нужно выполнить задолго до того, как ответ будет отправлен.
Если вы не проводили аутентификацию дома, тогда вы могли бы использовать предоставленную Java EE аутентификацию, управляемую контейнером, которая должна быть объявлена <security-constraint>
записями в web.xml
. Обратите внимание, что это также отключено от JSF, но это, по крайней мере, экономит вас от самообслуживания фильтра сервлета и управляемого bean.
Общий подход заключается в группировке ограниченных страниц за определенным шаблоном URL, например /app/*
, /private/*
, /secured/*
и т.д., и воспользоваться тем преимуществом, что JSF хранит сеанс с привязкой beans как HttpSession
атрибуты. Представьте, что у вас есть управляемый сеанс сеанса JSF bean UserManager
, который содержит зарегистрированного пользователя, тогда вы можете проверить его следующим образом:
@WebFilter(urlPatterns={"/app/*"})
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
UserManager userManager = (session != null) ? (UserManager) session.getAttribute("userManager") : null;
if (userManager == null || !userManager.isLoggedIn()) {
response.sendRedirect(request.getContextPath() + "/login.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
// ...
}
Если вы используете JSF 2.2+, есть другой способ управления ответом прямо перед его отправкой. Вы можете использовать <f:viewAction>
. Поместите следующее в ваше мнение:
<f:metadata>
<f:viewAction action="#{authenticator.check}" />
</f:metadata>
с
@Named
@RequestScoped // Scope doesn't matter actually. The listener will always be called on every request.
public class Authenticator {
public String check() {
if (authenticated) {
return null;
}
else {
return "login?faces-redirect=true";
}
}
// ...
}
Гарантируется, что он будет запущен до того, как ответ будет обработан. В противном случае, когда вы выполняете задание, например. @PostConstruct
, тогда вы рискуете java.lang.IllegalStateException: response already committed
, когда bean создается в первый раз, когда ответ уже частично отображен (и зафиксирован).
Я бы не стал считать это "лучшей" практикой, когда дело касается обработки HTTP-аутентификации. Это делает его слишком жестким, связанным с JSF. Вы должны продолжать использовать фильтр сервлета. Но для других целей это может быть хорошо.