JSF Filter skip some files - jsf

I have a project with this structure (I can't change it):
Web Pages
META-INF
WEB-INF
assets (javascript, css and images)
includes (top.xhtml, bottom.xhtml, etc)
templates (master.xhtml)
views
fornecedor
-home.xhtml
-user.xhtml
-login.xhtml
franqueador
-home.xhtml
-user.xhtml
-login.xhtml
O have a login.xhtml for each folder for a reason, I can't change it, it was passed by the project manager.
This is my SessionFilter:
#WebFilter("/views/*")
public class SessionFilter 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);
LoginController loginController = (LoginController) ((boolean) (session != null) ? session.getAttribute("loginController") : null);
if (!request.getRequestURI().endsWith("/fornecedor/index.xhtml")) {
if (loginController == null || !loginController.getIsLoggedIn()) {
response.sendRedirect(request.getContextPath()
+ "/views/index.xhtml");
} else {
chain.doFilter(req, res);
}
}
}
}
And it doesn't work, returns a blank page with no html code and when I change the .xhtml to .html, it gives me a redirect loop.
I need to avoid all my login.xhtml pages and the index.xhtml on views folder. I have debugged the if on my filter but it always returns false.
EDIT
Following BalusC answer I came to this:
if (!request.getRequestURI().endsWith("/views/index.html")
&& !request.getRequestURI().endsWith("/views/fornecedor/login.html")
&& !request.getRequestURI().endsWith("/views/franqueado/login.html")
&& (loginController == null || !loginController.getIsLoggedIn())) {
response.sendRedirect(request.getContextPath() + "/views/index.html");
} else {
chain.doFilter(req, res);
}
It is working but there is another problem, if I have 10 folder, I'll need to add them on this if statement...I need to make it automatic.

Your first if condition has no else. That explains the blank page.
You need to evaluate the URL and logged-in conditions in a single if instead.
if (!request.getRequestURI().endsWith("/fornecedor/index.xhtml") && (loginController == null || !loginController.getIsLoggedIn()) {
response.sendRedirect(request.getContextPath() + "/views/index.xhtml");
} else {
chain.doFilter(req, res);
}
As to the redirect loop, that's because your FacesServlet is mapped on an URL pattern of *.html instead of *.xhtml for some unclear reason. So the request URL does never match. Fix the URLs in the filter accordingly. To generalize it for all login.html and index.html requests, just do as follows:
if (!request.getRequestURI().endsWith("/index.html")
&& !request.getRequestURI().endsWith("/login.html")
&& (loginController == null || !loginController.getIsLoggedIn())
{
response.sendRedirect(request.getContextPath() + "/views/index.html");
} else {
chain.doFilter(req, res);
}

Related

Jmeter + JSF can't pass login filter

I got a problem when use Jmeter to test a JSF application.
I'm a newbie in Jmeter, follow some post on net i did
And code filter
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
try {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession httpSession = httpRequest.getSession(false);
if (!httpRequest.getRequestURI().startsWith(httpRequest.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
httpResponse.setHeader("Pragma", "no-cache"); // HTTP 1.0.
httpResponse.setDateHeader("Expires", 0); // Proxies.
}
String strRequestURI = httpRequest.getRequestURI();
// httpRequest.getServletPath()
// httpRequest.getServerName()
if (strRequestURI.indexOf("/public/") >= 0)
chain.doFilter(request, response);
else if ( (strRequestURI.indexOf("/login.xhtml") >= 0)
|| strRequestURI.contains("javax.faces.resource")
|| (strRequestURI.indexOf("/admin-access-denied.xhtml") >= 0)) {
chain.doFilter(request, response);
} else if ((httpSession != null) && (httpSession.getAttribute(SessionBean.SESSION_KEY) != null)) {
if (strRequestURI.indexOf("/lazy-load.xhtml") >= 0) {
chain.doFilter(request, response);
} else {
chain.doFilter(request, response);
}
} else {
httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.xhtml");
}
} catch (Exception e) {
e.printStackTrace();
}
I did all things that i read : add Coockie Manager, add Regular Expression Extrator, add parameter to Http request but after run test, my response data is blank ( only access to login.xhtml page )
But response is blank , only login page is showed .
Pls help me to solve this problem.
PS: I run debug mode and httpSession.getAttribute(SessionBean.SESSION_KEY) is always null , its set on login controller ( when login successed ).
HttpSession session = SessionBean.getSession();
session.setAttribute(SessionBean.SESSION_KEY, sessionData);
Thanks a lot,
Most likely your javax.faces.Viewstate value is not propertly correlated, you're sending ${viewstate} instead of the extracted value, most likely your Regular Expression fails somewhere
It's better to put PostProcessors as children of the specific Samplers, otherwise they are being triggered on each sampler causing execution overhead and in some cases JMeter Variables data loss
So your test should have the following structure:
lazy-load.xhtml
Extract Viewstate
login.xhtml
You may want to consider using CSS/JQuery Extractor or XPath Extractor instead of Regular Expressions Extractor as Regular Expressions are fragile, sensitive to markup change and complex ones can hardly be understood and maintained.
Example expressions:
XPath: //input[#id='javax.faces.ViewState]/#value
CSS:
- Expression: input[id=javax.faces.ViewState]
- Attribute: value

JSF permanent redirect using a bean on a preRenderView event

Is there a way to permanently redirect to a page on preRenderEvent view using faces-config navigation?
For example, after login it will redirect on a page and on that page I need to know the role of a user so that I can redirect him to the correct page.
I'm using this to redirect:
<f:metadata>
<f:event listener="#{loginRedirectBean.redirect}" type="preRenderView"></f:event>
</f:metadata>
public void redirect() throws IOException {
if (identity.isLoggedIn()) {
if(hasRole("admin")) {
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
context.redirect("faces.admin");
}
}
}
You should so make use of "if else" statements based on the user role :
public void redirect(){
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
if (context.isUserInRole("admin"){
context.redirect("adminpage");
}
else if (context.isUserInRole("user"){
context.redirect("userpage");
}
// other possible roles
else context.redirect("homepage");
}
Unrelated : In order to avoid firing double requests to the server whenever a user is logging in, I suggest to transfer that logic to be implemented within the method redirect(), to the method responsible of user logging with "if else" statements and with a String returning outcome (without the need to redirect manually as above) to specify the right page name to redirct to depending on the user role in "if" instruction.
Since faces-config navigation. I've just used a file given a role.
public void redirect() throws IOException {
String redirectUrl = "login.jsf";
if (identity.isLoggedIn()) {
try {
User user = ((PicketlinkUser) identity.getAccount()).getUser();
if (user.hasRole(Role.ROLE_ADMIN)) {
redirectUrl = "pages/secured/" + Role.ROLE_ADMIN + "/index.jsf";
} else if (user.hasRole(Role.ROLE_BRIDE)) {
redirectUrl = "pages/secured/" + Role.ROLE_BRIDE + "/index.jsf";
} else if (user.hasRole(Role.ROLE_VENDOR)) {
redirectUrl = "pages/secured/" + Role.ROLE_VENDOR + "/index.jsf";
} else if (user.hasRole(Role.ROLE_COLLABORATOR)) {
redirectUrl = "pages/secured/" + Role.ROLE_COLLABORATOR + "/index.jsf";
} else {
log.info("user={} has no valid role?", user);
redirectUrl = "index.jsf";
}
} catch (NullPointerException e) {
log.error("no role?={}", e.getMessage());
}
} else {
log.error("isNotLoggedIn, redirect to login.");
}
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
context.redirect(redirectUrl);
}

JSF page style missing when using login filter

I am using following filter to control access to all pages in JSF 2.0 using GlassFish as application server. The problem is that with this code although filter works fine and user are redirected to log.xhtml if they try to acess anyother page directly but the login.xhtml does not look good (no colored image displayed and while page shape changed) as it should be. However if i remove the sendRedirect statement and replace it with chain.doFilter statement, then the page displays in the same way as it should be looking nice and good however filtering does not work obviously. How can I fix this problem?
LoggingFilter.java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
LoginBean auth = (LoginBean) req.getSession().getAttribute("loginBean");
if ((auth != null && auth.isLoggedIn()) || req.getRequestURI().endsWith("/login.xhtml")) {
// User is logged in, so just continue request.
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
httpResponse.setHeader("Pragma", "no-cache"); // HTTP 1.0.
httpResponse.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(request, response);
} else {
// User is not logged in, so redirect to index.
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect(req.getContextPath() + "/faces/login.xhtml");
//FacesContext.getCurrentInstance().getExternalContext().dispatch("/login.xhtml");
//chain.doFilter(request, response);
}
}
This filter also redirects all requests on CSS/JS/image files to the login page. The browser end up getting a response containing some HTML code representing the login page instead of the concrete CSS/JS/image content it requested for and hence the browser fails applying the necessary look'n'feel.
Provided that you're 100% utilizing JSF resource management (<h:outputStylesheet>, etc) and thus they are all covered by /javax.faces.resource/* URIs, rewrite your filter as follows:
#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);
LoginBean auth = (session != null) ? session.getAttribute("loginBean") : null;
String loginURL = request.getContextPath() + "/faces/login.xhtml";
boolean loggedIn = auth != null && auth.isLoggedIn();
boolean loginRequest = request.getRequestURI().equals(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + "/faces" + ResourceHandler.RESOURCE_IDENTIFIER);
if (loggedIn || loginRequest || resourceRequest)) {
if (!resourceRequest) {
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);
} else {
response.sendRedirect(loginURL);
}
}
Note that no-cache headers should not be set on resource requests, otherwise you defeat the benefit of the browser cache on CSS/JS/image files.

How to restrict access if user is not logged in

My project has a template main.xhtml and three views login.xhtml, dashboard.xhtml, new.xhtml. Once I login in login.xhtml, the LoginBean will validate and if successful, then it will take to dashboard.xhtml. If user need to create an new record he click the new button which takes to new.xhtml.
But the problem is, if dashboard.xhtml is requested directly from browser, then it is working without login. Do I need to check every view that the user is logged in? How can I achieve this?
It sounds like as if you're homegrowing authentication. In that case, you need to also homegrow access restriction. That is normally to be done using a servlet filter.
Assuming that you're logging in as follows in a #RequestScoped bean,
public String login() {
User user = userService.find(username, password);
FacesContext context = FacesContext.getCurrentInstance();
if (user != null) {
context.getExternalContext().getSessionMap().put("user", user);
return "dashboard.xhtml?faces-redirect=true";
} else {
context.addMessage(null, new FacesMessage("Unknown login, try again."));
return null;
}
}
Then you can check for the logged-in user in a #WebFilter("/*") filter as follows:
#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);
User user = (session != null) ? session.getAttribute("user") : null;
String loginURL = request.getContextPath() + "/login.xhtml";
boolean loginRequest = request.getRequestURI().startsWith(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER);
if (user != null || loginRequest || resourceRequest)) {
chain.doFilter(request, response);
} else {
response.sendRedirect(loginURL);
}
}
Note thus that this would continue the request when the user is logged in, or when the login page itself is requested directly, or when a JSF resource (CSS/JS/image) is been requested.
If you were using container managed authentication, then the filter would have been unnecessary. See also How to handle authentication/authorization with users in a database?

How to avoid ;jsessionid=XXX on the first call to a page? it works if first page is jsp

I have an application which uses the welcome-page index.jsp with an <iframe></iframe> the contents of the iframe is a jsf page. If I access index.jsp I see a cookie already on the first get in firebug:
Set-Cookie JSESSIONID=C615DA89B6EF73F801973EA3DCD3B226; Path=/
The page of the <iframe> inherits this jsessionid. BUT: when I directly access the page of the <iframe/> I get the jsessionId rewritten to all URLs without a cookie - on the first request. Afterwards the cookie is used. This is all fine - if:
The security system would allow me to perform url rewrites.
I run jboss 4.2.2
I want to achieve the same behaviour as I have with the index.jsp - e.g. always use cookies and always avoid http rewrite.
[EDIT]
thanks to balusc's answer I wrote this:
public class JsessionIdAvoiderFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
boolean allowFilterChain = redirectToAvoidJsessionId((HttpServletRequest) req, (HttpServletResponse)res);
//I'm doing this because if I execute the request completely, it will perform a pretty heavy lookup operation. No need to do it twice.
if(allowFilterChain)
chain.doFilter(req, res);
}
public static boolean redirectToAvoidJsessionId(HttpServletRequest req, HttpServletResponse res) {
HttpSession s = req.getSession();
if(s.isNew()) {
//after the redirect we don't want to redirect again.
if(!(req.isRequestedSessionIdFromCookie()&&req.isRequestedSessionIdFromURL()))
{
//yeah we have request parameters actually on that request.
String qs = req.getQueryString();
String requestURI = req.getRequestURI();
try {
res.sendRedirect(requestURI+"?"+qs);
return false;
} catch (IOException e) {
logger.error("Error sending redirect. " + e.getMessage());
}
}
}
return true;
}
}
Don't forget to add it to your web.xml
<filter>
<display-name>JsessionId Filter</display-name>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<filter-class>my.namespace.JsessionIdAvoiderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
Since Servlet 3.0 you could use <tracking-mode>COOKIE</tracking-mode> for this. But as JBoss 4.2.2 isn't Servlet 3.0 compilant, this isn't an option.
Easiest would be to create a servlet filter which sends a redirect to HttpServletRequest#getRequestURI() when HttpSession#isNew() returns true. Don't forget to check the HttpServletRequest#isRequestedSessionIdFromCookie() to prevent an infinite redirect loop when the client doesn't support cookies at all.
Based on Christopher Schultz recommendation I tried this and it works.
package com.rama.test.jsessionfilter
public class JsessionIdAvoiderFilter implements Filter {
protected static final Logger LOGGER = LogManager.getLogger(JsessionIdAvoiderFilter.class);
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
if (!(req instanceof HttpServletRequest)) {
chain.doFilter(req, res);
return;
}
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Redirect requests with JSESSIONID in URL to clean old links
/* If you really want clean up some old links which have Jsession id bookmarked clean it. If its new app
this below check is not required. */
if (request.isRequestedSessionIdFromURL()) {
String url = request.getRequestURL().append(request.getQueryString() != null ? "?"
+ request.getQueryString() : "").toString();
response.setHeader("Location", url);
response.sendError(HttpServletResponse.SC_MOVED_PERMANENTLY);
LOGGER.info(" Found url with jsession id in it:"+ request.getRequestURL() +": url="+url);
return;
}
// Prevent rendering of JSESSIONID in URLs for all outgoing links
HttpServletResponseWrapper wrappedResponse = new HttpServletResponseWrapper(
response) {
#Override
public String encodeRedirectUrl(String url) {
return url;
}
#Override
public String encodeRedirectURL(String url) {
return url;
}
#Override
public String encodeUrl(String url) {
return url;
}
#Override
public String encodeURL(String url) {
return url;
}
};
chain.doFilter(req, wrappedResponse);
}
public void destroy() {
}
public void init(FilterConfig arg0) throws ServletException {
}
}
and the following entry in web.xml
<filter>
<display-name>JsessionId Filter</display-name>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<filter-class>com.rama.test.jsessionfilter.JsessionIdAvoiderFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>jsessionIdAvoiderFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Works great !!!.
This can be done with a simple Filter that wraps the request with an HttpServletRequest which overrides HttpServletRequest.encodeURL and HttpServletRequest.encodeRedirectURL. Simply return the String argument passed to it and you will disable URL re-writing. Note that this will only work for a single webapp unless you want to either configure it in conf/web.xml (not recommended) or configure it in all of your separate webapps.
This technique is superior to that posted later in your question because it does not require redirection which can slow-down your requests. IMO, it's also cleaner.

Resources