How implement a login filter in JSF?
Solution 1
You need to implement the javax.servlet.Filter
class, do the desired job in doFilter()
method and map it on an URL pattern covering the restricted pages, /user/*
maybe? Inside the doFilter()
you should check the presence of the logged-in user in the session somehow. Further you also need to take JSF ajax and resource requests into account. JSF ajax requests require a special XML response to let JavaScript perform a redirect. JSF resource requests need to be skipped otherwise your login page won't have any CSS/JS/images anymore.
Assuming that you've a /login.xhtml
page which stores the logged-in user in a JSF managed bean via externalContext.getSessionMap().put("user", user)
, then you could get it via session.getAttribute("user")
the usual way like below:
@WebFilter("/user/*")
public class AuthorizationFilter implements Filter {
private static final String AJAX_REDIRECT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<partial-response><redirect url=\"%s\"></redirect></partial-response>";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURL = request.getContextPath() + "/login.xhtml";
boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
boolean loginRequest = request.getRequestURI().equals(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER + "/");
boolean ajaxRequest = "partial/ajax".equals(request.getHeader("Faces-Request"));
if (loggedIn || loginRequest || resourceRequest) {
if (!resourceRequest) { // Prevent browser from caching restricted resources. See also https://stackoverflow.com/q/4194207/157882
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response); // So, just continue request.
}
else if (ajaxRequest) {
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().printf(AJAX_REDIRECT_XML, loginURL); // So, return special XML response instructing JSF ajax to send a redirect.
}
else {
response.sendRedirect(loginURL); // So, just perform standard synchronous redirect.
}
}
// You need to override init() and destroy() as well, but they can be kept empty.
}
Additionally, the filter also disabled browser cache on secured page, so the browser back button won't show up them anymore.
In case you happen to use JSF utility library OmniFaces, above code could be reduced as below:
@WebFilter("/user/*")
public class AuthorizationFilter extends HttpFilter {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws ServletException, IOException {
String loginURL = request.getContextPath() + "/login.xhtml";
boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
boolean loginRequest = request.getRequestURI().equals(loginURL);
boolean resourceRequest = Servlets.isFacesResourceRequest(request);
if (loggedIn || loginRequest || resourceRequest) {
if (!resourceRequest) { // Prevent browser from caching restricted resources. See also https://stackoverflow.com/q/4194207/157882
Servlets.setNoCacheHeaders(response);
}
chain.doFilter(request, response); // So, just continue request.
}
else {
Servlets.facesRedirect(request, response, loginURL);
}
}
}
See also:
- Our Servlet Filters wiki page
- How to handle authentication/authorization with users in a database?
- Using JSF 2.0 / Facelets, is there a way to attach a global listener to all AJAX calls?
- Avoid back button on JSF web application
- JSF: How control access and rights in JSF?
Solution 2
While it's of course legitimate to use a simple Servlet filter, there are alternatives like
![Valter Silva](https://i.stack.imgur.com/DzsTt.jpg?s=256&g=1)
Valter Silva
Updated on December 14, 2020Comments
-
Valter Silva over 3 years
I would like to block the access of some page even if the user knows the url of some pages. For example,
/localhost:8080/user/home.xhtml
(need to do the login first) if not logged then redirect to/index.xhtml
.How do that in JSF ? I read in the Google that's needed a filter, but I don't know how to do that.
-
Jake Long almost 12 yearsinstead of using getAttribute("auth"), could you use @ManagedProperty(value="#{auth}") private Auth auth;
-
BalusC almost 12 years@Jake: Wrong.
@ManagedProperty
works in a@ManagedBean
only, not in a@WebFilter
. -
BalusC almost 12 years@Jake: If you're using CDI's
@Named
instead, then you can use@Inject
to inject it in both a@ManagedBean
(or just another@Named
) and a@WebFilter
. -
Apoorv Kansal over 11 years@BalusC: Would we have to worry about thread safety in this technique?
-
maurizeio over 8 years@BalusC If filter is mapped to
/user/*
and login page is located at/login.xhtml
I suppose that login requests won't be captured. Login page should be located at/user/login.xhtml
but then I ask myself: why do we need to protect the login page? -
maurizeio over 8 years@BalusC same argument applies to resource requests.