I am trying to implement a "generic" view where (part of) the content displayed depends on the URL. E.g.
If /somepath/somepage.xhtml points to a non existing file, instead of going straight to a 404 error I want to try to retrieve /somepath/somepage.xhtml's content from the database using a generic view, /genericview.xhtml, where I have something like:
<h:outputText value="#{genericViewBean.content_lg}"
escape="false" />
which, if found by the backing bean, would output the content of the database entry from a tgenericcontent table, depending on the originally requested viewId:
webpath | content
/somepath/somepage.xhtml | <p>This is a test</p>
/someotherpath/someotherpage.xhtml | <p>A different test</p>
If the view content is not found in that table then the standard 404 error would be returned.
The closest I got wast to clone /genericview.xhtml changing only the file path (for example, to /somepath/somepage.xhtml). But that gets me one exact copy of the file per view, it is quite messy, and it doesn't allow me to create a new url just by adding an entry to my database.
How can I get the same result without cloning /genericview.xhtml?
(P.S: I have read about prettyfaces, but isn't there a simpler solution?)
For that, normally a servlet filter is being used. PrettyFaces, UrlRewriteFilter and FacesViews also do it that way.
You can get the request URI by HttpServletRequest#getRequestURI(). You can check the existence of a web resource by ServletContext#getResource() which will return null on non-existent resources. If the resource exists, just continue the request by FilterChain#doFilter(), else forward the request to the generic view by RequestDispatcher#forward().
All in all, this is how the filter could look like:
#WebFilter("/*")
public class GenericViewFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String relativeRequestURI = request.getRequestURI().substring(request.getContextPath().length());
boolean resourceExists = request.getServletContext().getResource(relativeRequestURI) != null;
boolean facesResourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER));
if (resourceExists || facesResourceRequest) {
chain.doFilter(request, response);
}
else {
request.getRequestDispatcher("/genericview.xhtml").forward(request, response);
}
}
// ...
}
In the /genericview.xhtml, the original request URI is available as request attribute keyed with RequestDispatcher#FORWARD_REQUEST_URI. You could use it in #PostConstruct of backing bean associated with the view in order to pull the right content from the DB.
String originalRequestURI = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
// ...
Related
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.
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?
While it's of course legitimate to use a simple Servlet filter, there are alternatives like
Spring Security
Java EE Security
Apache Shiro
A week ago, I have studied about ViewExpiredException, and I've read several things about it.
viewExpiredException JSF
How to control web page caching, across all browsers?
Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request
My problem, which is some cases, I would like to ignore the ViewExpiredException. These are situations that do not need a "session", and my Beans are #RequestScoped. As an example, the pages login.xhtml, register.xhtml and passwordRecovery.xhtml.
In these cases, it is very strange display an error to the user saying that your session has expired. So if you open the login page and stand still for a while, when he inform your data and click Login, it would be forwarded to an error page. I would just ignore it and let transparent to the user.
So, my solution so far is create a ExceptionHandler to ignore these exceptions:
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
// just remove the exception from queue
if (t instanceof ViewExpiredException) {
i.remove();
}
}
getWrapped().handle();
}
Then, I created a filter to check whether the user is logged in, if not then redirected to login page (This filter applies only pages that require authentication):
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!loginManagedBean.isLogged()) {
String pathLogin = request.getContextPath() + "/" + LOGIN_VIEW;
if (isAJAXRequest(request)) {
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>", pathLogin);
return;
}
pathLogin += "?source=" + request.getServletPath();
response.sendRedirect(pathLogin);
return;
}
chain.doFilter(request, response);
}
So when the session expires, does not affect the user experience in the login and registration pages. And on pages I wish session, are handled by the filter.
That would be a good solution? Are there any security risk to ignore ViewExpiredException in a ExceptionHandler?
Ignoring them is not technically bad in this specific case, but it indicates a bad design. It's as if you're using the wrong tool for the job. I.e. those views should actually never expire.
Just make specifically those views stateless.
<f:view transient="true">
...
</f:view>
This can be placed anywhere in the page, even duplicated, but most self-documenting is making it the top level tag of the page, composition or definition.
See also:
What is the usefulness of statelessness in JSF?
I have created a Filter listening on an url-pattern of /* which replaces the HttpServletRequest with a HttpServletRequestWrapper implementation.
I have a Servlet and in this Servlet am using h:graphicImage to render images fetching from Apache web server.
<h:graphicImage value="/locationInMyWebServer/myImage.jgp"></h:graphicImage>
When I hit the URL for accessing this page (containing image), the image was not getting displayed as JSESSIONID was getting appended to my image name. The URL that was getting formed was like below.
http:/myDomain/myServlet/../myImage.jpg;JSESSIONID=ABCDEFGHIJKLMM
Hence, I have used the Filter (more details about this filter is here) as stated in the beginning of my question.
From this Servlet there is a link for logging in. When a User logs in, same JSESSIONID is getting retained even after authentication. Since, Session ID is same before logging in and after a user logs in, this is leading to Session-fixation attacks.
How can I avoid using this filter and also solve my problem of JSESSIONID getting appended to images when I use h:graphicImage
PS: I can't use <img src> because my h:graphicImage is inside h:commandLink
Session Id was different before logging in and after logging in , before using this Filter
I have added the relevant code below.
Below code is from my web.xml which has entry for Filter
<filter>
<filter-name>URLSessionFilter</filter-name>
<filter-class>myPackage.web.filter.URLSessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>URLSessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
Code in my URLSessionFilter is below,
public class URLSessionFilter implements Filter
{
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
if (!(request instanceof HttpServletRequest))
{
chain.doFilter(request, response);
return;
}
HttpServletResponse httpResponse = (HttpServletResponse)response;
HttpServletResponseWrapper wrappedResponse = new HttpServletResponseWrapper(httpResponse)
{
public String encodeRedirectUrl(String url)
{
return url;
}
public String encodeRedirectURL(String url) {
return url; }
public String encodeUrl(String url) {
return url; }
public String encodeURL(String url) {
return url;
}
};
chain.doFilter(request, wrappedResponse);
}
public void init(FilterConfig filterConfig)
{
}
public void destroy()
{
}
}
There is a link in my Servlet on click of which login page will be displayed. Code is below,
<h:commandLink action="#{myBean.myMethod}">
<h:graphicImage value="/myLocInWebserver/myImage.jpg">
</h:commandLink>
In myBean.myMethod , am doing some DB clean up activities and redirecting to login page.
Another way is avoid the servlet container interpreting something as a URL. To accomplish that, you would avoid any of the special JSP or JSF tags, and directly use HTML tags. In your case - that could look like follows:
<h:commandLink action="#{myBean.myMethod}">
<img src="#{request.contextPath}/myLocInWebserver/myImage.jpg"/>
</h:commandLink>
No more <h:graphicImage> ...
You would still want your context path to be prefixed without any hardcoding - hence the use of #{request.contextPath}.
I recently came to this solution, as I was integrating JavaMelody with my application, and provided a link for admins to the tool. However somehow JavaMelody fails with the ;jsessionid appended. Hence, I am currently generating the URL as follows:
<a href="#{request.contextPath}/monitoring"
target="_blank"
class="ui-link ui-widget"
>
Java Melody Performance Monitoring
</a>
instead of the typical JSF solution
<p:link value="Java Melody Performance Monitoring"
href="/monitoring"
target="_blank"
/>
Which simply won't work.
The benefit of this solution is that I can now control this on a URL by URL basis, and I do not have to worry about setting <tracking-mode>COOKIE</tracking-mode> globally.
As I was told and found on websites, my bean will only be executed if I have a call for it on my .xhtml.
Is it possible to call my bean without any EL expression?
I need this because my HomeController is calling a method that checks for the session status and on my home.xhtml and don't have any need for fall this bean, for now.
You need to look for a solution in a different direction.
If you're homegrowing user authentication instead of using container managed authentication, then you normally use a servlet filter for the particular purpose of checking if an user is logged in or not.
The servlet filter can like as servlets (like FacesServlet) be mapped on a particular URL pattern. It will then be invoked on every request matching that URL pattern. You can explore request/session data in the filter and then handle the request/response accordingly by either continuning the filter chain, or by sending a redirect.
You need to implement javax.servlet.Filter interface accordingly. Here's a kickoff example of how the doFilter() method should be implemented assuming that you've a #SessionScoped managed bean LoginController. JSF stores session scoped managed beans as attributes of HttpSession.
#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);
LoginController loginController = (LoginController) (session != null) ? session.getAttribute("loginController") : null;
if (loginController == null || !loginController.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.
}
}
Map this filter on an URL pattern covering the restricted pages, e.g. /app/*.
#WebFilter("/app/*")
public class LoginFilter implements Filter {
// ...
}
Update if the login.xhtml is also covered by this URL pattern and you really can't change it, then change the if block as follows:
if (!request.getRequestURI().endsWith("/login.xhtml") && (loginController == null || !loginController.isLoggedIn())) {
// ...
}
i have a Backing Bean in which i read parameter which are not bound to a component. Seam offered that to read get parameter. I used
#RequestParameter private String param1;
this way he doesn't skip my action method on a validation error, like when i used
<param name="param1" value="#{myBean.param1}" />
, because i don't want to render HTML responses but reuse the business logic. Instead i grab the response stream and write the rendered response myself
FacesContext ctx = FacesContext.getCurrentInstance();
final HttpServletResponse resp = (HttpServletResponse)ctx.getExternalContext().getResponse();
resp.getOutputStream().write(xml.getBytes());
resp.getOutputStream().flush();
resp.getOutputStream().close();
ctx.responseComplete(); // render response phase done
I tried to read the file from the HttpServletRequest inputStream but that was already empty. Is there a way to still get the "file" from JSF. I could use a separate servlet and handle it there, but that would break a little bit how i build the rest of the interface.
Thx for any advice.
Edit: Just for completeness the code for the input stream
final HttpServletRequest request = (HttpServletRequest)ctx.getExternalContext().getRequest();
ServletInputStream stream = request.getInputStream();
int byt = stream.read(); // byt was -1
Edit Edit: Sorry for the long post. Now i made a separate servlet. There i also get an empty input Stream even though Firefug said i transmitted the file. How can that be empty?
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
ServletInputStream stream= req.getInputStream();
int test = stream.read(); // test == -1
That servlet code works fine in a separate project, so one of the JSF/Seam servlets in the filter chain has to remove the data. If someone has a hint which one or how to check.
The SeamFilter or more exactly the MultipartFilter was checking every request if it is a POST and multipart request. If so, they wrapped the request in a MultipartRequest and put the files in a separate parameter map.
final HttpServletRequestWrapper request = (HttpServletRequestWrapper)FacesContext.getCurrentInstance().getExternalContext().getRequest();
final ServletRequest innerRequest = request.getRequest();
if(innerRequest instanceof MultipartRequest){
byte[] imageData = ((MultipartRequest)innerRequest).getFileBytes("image");