Important post today 🙂
How to create a flawless login logout with JSF Facelets and a Java Application Server (JBoss, Glassfish) without much effort. Previously i’ve posted a similar article but small errors persisted. This post corrects that.
Four items are required:
1) Include a <META HTTP-EQUIV=”refresh” CONTENT=”15″> on your login page. This is make your login page refresh preventing an error with our methodology regarding context.
2) Edit your web.xml with the following. This will indicate that when a ViewExpiredException happens then the browser will redirect the client to its login page
<error-page> <exception-type>javax.faces.application.ViewExpiredException</exception-type> <location>/faces/login.xhtml</location> </error-page>
Also add a session timeout:
<session-config> <session-timeout> 30 </session-timeout> </session-config>
3) Implement a phase-listener for checking your credentials and preventing pagejumps. First, add the following to your faces-config.xml:
<lifecycle> <phase-listener>yourpackge.security.AuthorizationListener</phase-listener> </lifecycle>
Here’s your Java Class:
public class AuthorizationListener implements PhaseListener { public void afterPhase(PhaseEvent event) { FacesContext facesContext = event.getFacesContext(); String currentPage = facesContext.getViewRoot().getViewId(); boolean isLoginPage = (currentPage.lastIndexOf("login.xhtml") > -1); HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(false); if(session==null){ NavigationHandler nh = facesContext.getApplication().getNavigationHandler(); nh.handleNavigation(facesContext, null, "loginPage"); } else{ Object currentUser = session.getAttribute("username"); if (!isLoginPage && (currentUser == null || currentUser == "")) { NavigationHandler nh = facesContext.getApplication().getNavigationHandler(); nh.handleNavigation(facesContext, null, "loginPage"); } } } public void beforePhase(PhaseEvent event) { } public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }
Im sum, this class will check if your session credentials are ok and if not, redirect you to the login page.This will happen everytime a request is made on your app. Btw, ‘LoginPage’ is a navigation handler for login.xhtml specified on faces-config.xml
4) To complement this you’ll only need a Authentication class which puts your credentials on session with the following:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("username", idUser);
To close it, simply do the following:
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
And that’s it! Hope it helps!! Cumps!! 😀
Very good and helpful post. Thank you.
I have one question though…
if i access without login http://192.168.2.5:8080/bookstore/faces/success.xhtml
i correctly get redirected to login page, if access
http://192.168.2.5:8080/bookstore/success.xhtml i get this output:
#{login_bean.username}!
success.xhtml only has #{login_bean.username}!
Any thoughts?
Hi! Thanks 🙂
That’s cause you’re using facelets. In your context, the dynamic code from JSF will only run when ‘/faces/’ is explicitly called. If you specify a page without the ‘/faces/’, that page will appear but all the dynamic code (like login_bean.username) which calls your backend won’t. This means your information should still be safe.
That being said, i can’t give a ‘verbatim’ workaround for this issue since the static information i place on the xhtmls is completely irrelevant by itself and i disregard this issue.
One option however can be the use of security-contrains. Check this page up – . If you can find a solution, let me know if you can. 🙂
Cheers!!
What if I am using container managed security? Will this still work if Glassfish handles my form based authentication?
Nice post by the way. Thanks
If you’re using container manager security then you probably don’t need the PhaseListener to check your credentials. All authentication issues are handled by Glassfish.
Check out this great tutorial on how to do it – Container Managed Security – Glassfish 🙂
Hi thanks for posting this interesting post
I have a question, how to redirect the user to the login page when he/she tries to access other pages of the app: that is if I have login.xhtml and welcome.xhtml, if the user attempt to access the welcome.xhtml page and he/she still not logged in , how to redirect him/her to the login page? thanks for help
Hi.
The answer to your question is precisely what this example does. 🙂
In other words, each time a call/request is made on the application, the Phase Listener is triggered. In this example, what i do is simply check if the credentials are already there or not. If not, i redirect the user to the login page. For instances, you haven’t logged in and you enter the welcome.xhtml URL. The Listener is triggered, checks that you aren’t logged and redirects you to the login page.
This example is done via Navigation Handlers. In addition to what is on this post, you need to place the following instruction on faces-config.xml and you’ll need your basic authentication class (typical login method).
Hope it helps.
Excelent post! Very useful.
Can I run it on a Tomcat 7 server? Would it be any issues if i run it on Tomcat?
Thanks for the help.
Yep. No Java EE here so no need for JBoss or Glassfish servers. A simple servlet container does just as good.
Hi,
I have tryed this post, and I think it is a simple a easy way to implement autentication on app in JSP-JSF.
I have one doubt:
Why it is necessary include a on my login page?
NOTE: I implemented this post in: Netbeans 6.8 / Apache Tomcat 6.0.20
I suppose you here referring to the META tag. Well, in every java app server, servlets/beans have timeouts. If you keep the page on for a long time and try to enter the credentials without refreshing it first, then you’ll get a XML Error. This happens because the server has cleaned the instantiations that are idle for a long time, something that the client – the browser – doesn’t know. So when you try to authenticate, it will reach nothing because the server is no longer there. By making periodic refreshes you keep that from happening. Ideally, your META refresh should match the servlet timeouts but as long as it’s smaller than it, no issues there.
Hi aquaryus, firts of all the is a very easy way to implement autentication, is great, just have a question, i have 2 pages: login and page2, when i loggin i’ll go to page2, where i can logout and it’ll take to login page, but if i hit the back it take me to the page2, if there any way to avoid that? even know that the user is not login? Thanks
Hi! Well, i can’t give you a solution for that but i can tell you that from the moment you logout, every option in page2 won’t work and will redirect you to the login page. Hitting the ‘back’ button works at the browser level. When you logout, your session in the server (which is what matters) goes null. So try and put a business operation on your page2, make a logout, and hit ‘back’. You’ll see that the PhaseListener will automatically redirect you to the login page 🙂
This Post is really So help full . I was stucking in this issue last one month. But found it here very perfect Code..
I want say Thank you so much
hi,
thnx for the post.
but may i implement it using eclipse?
whenevr im tryn to login its generating an error “Oops! This link appears to be broken.” !!
plz help
Hi. Well if that is happening, you must be redirecting the user to a page that doesn’t exist. Check your links syntax.
As for Eclipse, sure you can. Just make sure all elements are there. 🙂
is wrong, it deletes information every 15 seconds, DONT USE IT!!!!!
And what information are you refering to? If what you mean to say is that in 15 seconds you don’t have enough time to enter your username and password and press login, simply adjust the value to a higher number. The meta tag simply avoids timeouts within the session beans in the Java container.
Cheers
Tardé solo 2 minutos haciendo que esto funcionara en mi proyecto, muchas gracias.
In just two minutes I did that on my project, thank you very much.
currentUser == “” is not a good check. This would check references and not object values and will lead every test to FALSE.
First two conditions are likely to be evaluated to true just before this (cause of lazy comparison). It would be better to do something like
“”.equals((String)currentUser))
This would be even more robust (even if not needed cause the null check just done).
Very helpful indeed!
Cheers.
very good post.
Excellent post..Thanks
Hi
Thanks for the post, it’s very useful.
fsicher
Thanks a lot, God bless you!!!
thank you! excellent explanation! congratulations!