I have a dynamic web application in Java EE with JSF, Facelets, Richfaces.
My pages are all xhtml pages.
So JSTL isn't working in it.
For my account pages and all other private pages to be reachable, I want to test if the user got connected, so if the attribute session in HttpSession is not null. If it's null, the user gets redirected in the welcome page.
I tried in my xhtml page :
<jstl:if test="${sessionScope['session']==null}">
<jstl redirect...>
</jstl:if>-->
but as it's not jsp page it won't work. So where am I supposed to test if the session is not null to allow the user to see his private pages ?
in a central managed bean ?
The normal place for this is a Filter.
Create a class which implementsjavax.servlet.Filter and write the following logic in the doFilter() method:
if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
// Not logged in, so redirect request to login page.
((HttpServletResponse) response).sendRedirect("/login.jsf");
} else {
// Logged in, so just continue request.
chain.doFilter(request, response);
}
Map this filter in web.xml on an url-pattern of something like /private/*, /secured/*, /restricted/*, etc.
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.example.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/private/*</url-pattern>
</filter-mapping>
If you have the private pages in the /private folder then this filter will be invoked and handle the presence of the logged-in user in the session accordingly.
Note that I renamed attribute name session to user since that makes much more sense. The HttpSession itself is namely already the session. It would otherise been too ambiguous and confusing for other developers checking/maintaining your code.
Related
Is it possible to prevent remote access of a web page?
Let's say I have my page1 (home page) which is obviously accessible anywhere via its url. Then I have page2 (admin page) which I would only like to be accessible from the machine where my web application is deployed.
This may sound like this kind of scenario. Only, instead of the admin console, page2 should not be accessible remotely.
Please be more precise what are you using?
I assume that you don't use any framework, so you have only serlvets and .jsp pages.
First,put you adminpage.jsp to WEB-INF folder. Content of this folder is not visible out of your application (without your permission).
Second, create filter that will intercept your requests to servlets (Try to use servlets for all requests, don't use direct links to .jsp pages, because this is only way to add some security. These servlets should be like controllers in MVC). To create Filter you need to add class that will implement interface Filter
public MyFilter implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
if (request.getRemoteAddr.equals("your server address") {
filterChain.doFilter(request,response);
}
}
...
}
You see, if IP address is equal as your server address is, this request will be proceed further. Filter interface has more two methods init() and destroy() and you can leave them blank. To connect your filter with your servlet add to your web.xml following.
<filter>
<filter-name>myFilter</filter-name>
<filter-class>fullPackagePath.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/url-to-the-admin-panel-servlet</url-pattern>
</filter-mapping>
Of course you need to have servlet with url that will forward to the admin.jsp page.
I have implemented a Filter for checking if a user is logged in or not by checking the session for a #SessionScoped bean. When I started testing it, I however, noticed that whenever I accessed one of my pages the Filter would be invoked multiple times.
I have figured out that I needed to ignore AJAX requests which reduced the number of times my filter would be invoked but the number of requests triggered each time I loaded the page was still more than one.
By trial and error, I have figured out that the requests would be generated by the following XHTML tags (both embedded in the <h:body> tag):
<h:outputStylesheet name="styles/userbar.css" target="head"/>
<o:commandScript name="updateMessages" render="custom_messages"/>
The second tag being part of OmniFaces library.
Any ideas why I get multiple requests or maybe if there is a way to ignore the requests generated by these tags?
Any help would be appreciated.
That can happen if you mapped the filter on a generic URL pattern like #WebFilter("/*"), or directly on the faces servlet like #WebFilter(servletNames="facesServlet"). The requests you're referring to are just coming from (auto-included) CSS/JS/image resources. If you track the browser's builtin HTTP traffic monitor (press F12, Network) or debug the request URI in filter, then that should have become clear quickly.
As to covering JSF resource requests, if changing the filter to listen on a more specific URL pattern like #WebFilter("/app/*") is not possible for some reason, then you'd need to add an additional check on the request URI. Given that you're using OmniFaces, you can use the Servlets utility class to check in a filter if the current request is a JSF ajax request or a JSF resource request:
#WebFilter("/*")
public class YourFilter extends HttpFilter {
#Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws IOException, ServletException {
if (Servlets.isFacesAjaxRequest(request) || Servlets.isFacesResourceRequest(request)) {
chain.doFilter(request, response);
return;
}
// ...
}
}
See also:
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same (contains a "plain vanilla" Servlet example for the case you aren't using OmniFaces)
I have a JSF page which posts data to an external page.
The data is loaded from a JSF managed bean which generates a unique ID in the post data.
I have an issue where a user clicks on a checkout button then navigates back to the same page and presses the checkout button again. The post data has not updated. Moreover, the bean is not invoked at all. Is there anyway to force JSF to reload the page and the form data?
<form action="#{checkoutBean.externalUrl}" method="post"
id="payForm" name="payForm">
<input type="hidden" value="#{checkoutBean.uniqueID}" />
<input type="submit" value="Proceed to Checkout" />
</form>
That page is likely being loaded from browser cache. This is essentially harmless, but indeed confusing to the enduser, because s/he incorrectly thinks that it's really coming from the server. You can easily confirm this by looking at the HTTP traffic monitor in browser's web developer toolset (press F12 in Chrome/FireFox23+/IE9+ and check "Network" section).
You basically need to tell the browser to not cache (dynamic) JSF pages. This way the browser will actually request the server for the page (and hereby triggering proper creation/initialization of managed beans and so forth) instead of showing the previously requested one from its cache.
Generally, this is to be done with a simple servlet filter like follows:
#WebFilter("/app/*")
public class NoCacheFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
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(req, res);
}
// ...
}
Where /app/* is the example URL pattern on which you'd like to turn off the browser cache. You can if necessary map it on /*, *.xhtml or even on servletNames={"Faces Servlet"}.
If you happen to use JSF utility library OmniFaces, then you can use its builtin CacheControlFilter by just adding the following entry to web.xml (which demonstrates a direct mapping on FacesServlet, meaning that every dynamic JSF page won't be cached):
<filter>
<filter-name>noCache</filter-name>
<filter-class>org.omnifaces.filter.CacheControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>noCache</filter-name>
<servlet-name>facesServlet</servlet-name>
</filter-mapping>
See also the showcase.
I found a solution that works for JSF without having to create a servlet-filter. Just put the line below to your .xhtml page.
<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('Cache-Control', 'no-cache, no-store')}" />
I am developing a web app using jsf technology. I want a method of my backing bean to be
invoked when the home page of my app is displayed. Usually, a backing bean method
gets involved when a user clicks on a link or a button.
In short I want my backing bean to get some data from the database and send it to the
jsf page, and I want this to occur when a user invokes the home page link
of my app.
Here is how I wanted to solve the problem: use a servlet that is executed when
the expected link is invoked, and call the backing bean from that servlet; but the problem
is I am having some difficulties with the url-pattern of the servlet in the web.xml file. The home page link of my app is: home.jsf. The name of my servlet is
HomeServlet and here is how I configured it in the web.xml file:
<servlet>
<servlet-name>HomeServlet</servlet-name>
<servlet-class>utils.HomeServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HomeServlet</servlet-name>
<url-pattern>/home.jsf</url-pattern>
</servlet-mapping>
So when I run the app, the servlet is executed, but my home.jsf page is empty (completely blank). I don't know why.
So what I want to know is:
Am I using the right approach for this issue?
If yes, which url-pattern should I use?
No, this is definitely not the right approach. You're working your way around JSF. You're supposed to do the job just in the constructor or #PostConstruct method of the request or view scoped JSF managed bean associated with the view.
E.g.
#ManagedBean
#RequestScoped
public class Home {
public Home() {
// Here.
}
#PostConstruct
public void init() {
// Or here. This will be invoked *after* any dependency injections,
// such as #EJB, #ManagedProperty, #Inject, #Resource, etc.
}
// ...
}
Whenever JSF encounters a #{home.someproperty} reference in the home.xhtml (or home.jsp) for the first time, then the bean will just be constructed.
When developing with JSF, you shouldn't have any need to develop other servlets. In JSF, the FacesServlet is the sole servlet which already does all the necessary request/response and model/view controlling job.
I'm looking for a best practise answer. I want to do some preprocessing for GET requests. So e.g. if the user is not allowed to see the page, redirect him to another page. But I don't want to use normal servlet filter, because I would like to express this behavior in the faces-config.xml. Is this possible and how is that called, how can it be done?
Can I define some Filter bean that also returns a String telling the faces-config.xml where to go next?
I googled for this but only hit on the normal filters. If I use filters, can a #WebFilter be a #ManagedBean at the same time? Or is that bad style?
If you're homegrowing HTTP request authentication on top of JSF, then a servlet filter is really the best approach. JSF is "just" a MVC framework and nothing in the JSF API is been specified to filter incoming HTTP requests to check user authentication. On normal GET requests, a JSF managed bean is usually only constructed when the HTTP response is about to be created and sent, or maybe already is been committed. This is not controllable from inside the managed bean. If the response is already been committed, you would not be able anymore to change (redirect) it. Authentication and changing the request/response really needs to be done far before the response is about to be sent.
If you were not homegrowing authentication, then you could have used the Java EE provided container managed authentication for this which is to be declared by <security-constraint> entries in web.xml. Note that this is also decoupled from JSF, but it at least saves you from homegrowing a servlet filter and a managed bean.
The general approach is to group the restricted pages behind a certain URL pattern like /app/*, /private/*, /secured/*, etc and to take the advantage of the fact that JSF stores session scoped beans as HttpSession attributes. Imagine that you've a JSF session scoped managed bean UserManager which holds the logged-in user, then you could check for it as follows:
#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.
}
}
// ...
}
If you're using JSF 2.2+, there's another way to control the response right before it is been sent. You can make use of the <f:viewAction>. Put the following somewhere in your view:
<f:metadata>
<f:viewAction action="#{authenticator.check}" />
</f:metadata>
with
#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";
}
}
// ...
}
This is guaranteed to be fired before the response is to be rendered. Otherwise when you do the job in e.g. #PostConstruct, then you may risk java.lang.IllegalStateException: response already committed when the bean is created for the first time when the response has already partially been rendered (and committed).
I only wouldn't consider it to be a "best" practice when it comes to handling HTTP authentication. It makes it too tight coupled into JSF. You should really keep using a servlet filter. But for other purposes, it may be fine.
See also:
When to use f:viewAction / preRenderView versus PostConstruct?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Limitations of using a PhaseListener instead of a Servlet Filter for authorization