Must close browser to get authentication data in PostConstruct method - jsf

I am trying to cache the authentication information in a SessionScoped managed bean.
When I open a browser and login into the server (the browser asks me for username/password) the first time, it works as it should.
The trouble comes when I restart the webapp or the server (it is a development setup). Then, accessing the webapp from one of the browser where I had previously logged in, causes that in my #PostConstruct method, FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal() returns null (I check that it is effectively executed).
On the other hand, if I just check that value from getUser(), it works correctly.
AFAIK, I expected the browser the just cache the credentials and that when I was reentering the application after a restart the only difference was that the browser would automatically send the credentials without prompting me again. I did not expect that it would make any difference in the server.
The code is the following (simplified)
#ManagedBean
#SessionScoped
public class UserManager {
private Principal userPrincipal = null;
#PostConstruct
public void init() {
this.userPrincipal =
FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
System.out.println("EN POSTCONSTRUCT DE LDAP PRINCIPAL!! " + this.userPrincipal);
}
public String getUser() {
return this.userPrincipal.getName();
}
}
The setup is JBoss 6.1 Final with Mojarra 2.03, JDK 6. I have tested it both with IE7 and Firefox.
UPDATE: I have found more about it. If I go into my welcome page, then the webapp works as expected. It is when I try to directly access another page (*1) that it fails to initialize the user info.
*1: I am not talking about reloading the page as this always fails, but typing http://myserver/mywebapp/page_that_is_not_welcome_one.xhtml in the URL bar.

Related

Java EE Container based Certificate authentication logout

In our Java EE application we use container based certificate authentication. We have created JAASLoginModule, which implements LoginModule interface with all required methods. We have configured our Wildfly and TomEE server to use this module both for authentication and ssl channel security, and everything goes smoothly with user login:
the user opens the browser and the app;
selects a certificate;
a JSF session is created, and now he is logged in;
A different story is with the logout. Just destroying the JSF session is not enough - after logout, if you just click back, the browser will get the certificate info from cache, recreate a session and lets you do the same stuff. Sometimes even browser restart does not help.
I could not find an effective way to call the logout method from the LoginModule from the JSF managed bean.
Any way to solve this problem?
Your problem is directly with the browser, so what you need is to tell the browser to "restart" the cache from your page every time it logs out, this, in order for it to think it's the first time the client is trying to get into that page. Kind of the same that private windows in Chrome and Firefox do.
Try this code:
//...
response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server
response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance
response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"
response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility
//can check userId or something likes this.In this sample, i checked with userName.
String userName = (String) session.getAttribute("User");
if (null == userName) {
request.setAttribute("Error", "Session has ended. Please login.");
RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
rd.forward(request, response);
}
Source: How to clear browser cache using java

JSF I'm Losing Session State When Changing From Managed To CDI

Edit: This is an epic face palm situation. Wrong import for SessionScoped. So tired last night while checking it I was sure I was using enterprise sessionscoped import while I was still using faces sessionscoped import. I'm leaving this up as an aid to doofuses like me. :)
It is early in this project. After implementing up to this point with managed beans, I changed my managed beans to CDI beans as this seems to be the latest consensus on the best way to do things But this has broken previously working code. I cannot for the life of me figure out why. Help and advice is appreciated.
Happy Path (Summary... detail below code extracts)
If user not logged in, show login or register links.
If user logged in show user preferences or logout links.
Now Crappy Path with CDI (I don't blame CDI)
If user not logged in, show login or register links.
If user logged in still see login or register links. (bad, bad app)
The objects involved are
a facelet menu panel (with a primefaces login dialog... I don't think this has any thing to do with it but included for completeness) with render attributes if logged in or not,
a session scoped user bean,
a request scoped authentication bean to log the user in and out.
Objects used listed below. Implemented as CDI beans.
facelet
<h:panelGroup id="loginPanel" rendered="#{!user.loggedIn}">
Show login buttons and stuff
</h:panelGroup>
<h:panelGroup id="logoutPanel" rendered="#{user.loggedIn}">
Show logout buttons and stuff
</h:panelGroup
authentication bean
#Named(value = "webAuthenticationBean") //formerly managedbean
#RequestScoped
public class WebAuthenticationBean implements Serializable {
#Inject private UserBean user; //formerly a managed property which worked
...
request.login(uername, password);
user.setuserdata(username); // sessionscoped user state here used to check login state among other things later.
...
return(true) // they are now logged in
user bean
#Named(value = "user") //formerly managedbean
#SessionScoped
public class UserBean implements Serializable {
#EJB
private UserService userService; //stateless session bean
private userInfo = new UserInfo(); // keeps user state and can be used as a DTO/VO
#PostConstruct
public void init() {
//sets default state to "guest user". This is NOT a logged in state
}
public void setuserdata(String username){
userInfo = userService.getUserInfo(username);
// method called from WebAuthenticationBean
// sets the user state to a non-guest user (they're logged in).
// I can see in debug mode that this is being called and retrieving
// the user data from the database and setting "userInfo"
}
public void isLoggedIn() throws InvalidUserException{
// checks state to see if they are logged in, basically a bit more than are they still a guest or not
returns (true) if logged in
returns (false) if not logged in
// this worked with managed beans
}
...
So here is the actual use case when I watch in debug mode:
Happy Path (prior to change to CDI bean)
1) User navigates to the welcome page
2) the user bean is queried to see if they are logged in (user.loggedIn in the facelet).
3) userbean checks logged in state. If they are still a guest they aren’t logged in.
4) They are identified as a guest so isLoggedIn() returns false.
5) Login button is shown.
6) User requests logs in
7) authentication bean begins login process: request.login returns successfully
8) authenticationbean sets user data: user.setuserdata(username) returns successfuly.
9) authentication bean loginMethod returns (they are logged userprincipal on the server)
Alternate (crappy) path branch here (happy path continues)
10) The menu rechecks login state (user.loggedIn)
11) userbean checks for appropriate state and sees they are valid non guest user
12) userbean returns (true) they are logged in
13) menu shows logout button
Crappy Path (what happens after I changed these to CDI beans)
10) The menu rechecks login state (user.loggedIn)
11) userbean checks for appropriate state and sees they are a guest //the updated user state seems to have disappeared from this user in this session.
12) userbean returns (false) they are not logged in //but they are
13) menu shows login button // they can’t login anyway since the server already sees them as logged in, in this session (ServletException: Attempt to re-login while the user identity already exists).
Why using managedbeans would I be able to see the userbean maintain its data in session scope but with cdi beans it does not? I am stumped. I’ll switch back to managed beans if I have to, it isn’t a big issue, but I would like to find out what I messed up.
I added some debugging code in the init method of the UserBean, and it appears as if the system is treating the SessionScoped UserBean as if it were RequestScoped. That is it is initializing on every call.
#PostConstruct
public void init() {
if (userInfo == null) {
userInfo = new UserInfoDTO();
userInfo.setUserName("Guest");
List<String> guestGroup = Arrays.asList(CoreUserGroupType.GUEST.toString());
userInfo.setUserGroups(guestGroup);
System.out.println("UserBean.init INSIDE Init If Statement");
}
System.out.println("UserBean.init OUTSIDE Init If Statement");
}
If it were really acting like it was SessionScoped the userInfo object would not be null every time and the 'if' statement would not be executed every time. But it is executing on every call to UserBean. So this is at the crux of the problem. As a matter of fact if it acted like it were in session scope it would not hit the init method at all on every call as it would still be initialized.
Am I not creating a sessionscoped bean properly? It would appear so, but I don't see how. As mentioned, this code ran fine when defined as a managedbean.
changed to the correct sessionscoped import and all is well. nothing hurt but my pride.

Redirect loop when liferay integrated with OpenSSO

My softwares are:
Liferay 6.0.6 with Tomocat 6.0.29, OpenSSO 9.5.2_RC1 Build 563 with tomcat 6.0.35, CentOS 6.2 Operating system
Setup:
I have setup both liferay and opensso on the same CenOS machine, making sure that both of its tomcat run on very different port, I have installed and configured OpenSSO with Liferay as per the guidelines availaible on liferay forums
Problem:
when i hit my application URL i get redirected to Opensso login page which is what i want, when i login with proper authentication details it trys to redirect to my application which is exactly how it should behave, however this redirect goes in a loop and i don't see my application dashboard. The conclusion i come to is that the redirect is trying to authenticate in liferay but somehow it does not get what it is looking for and goes back to opensso and this repeats infinitely. I can find similar issues been reported here. Unfortunetly, it did not work.
Later i decided to debug the liferay code and i put a break point on com.liferay.portal.servlet.filters.sso.opensso.OpenSSOUtil and com.liferay.portal.servlet.filters.sso.opensso.OpenSSOFilter. The way i understand this code is written is it first goes to the OpenSSOUtil.processFilter() method which get's the openSSO setting information that i have configured on liferay and later checks if it is authenticated by calling the method OpenSSOUtil.isAuthenticated(). This particular implementation basically reads the cookie information sent and tries to set the cookie property on liferay by calling the method OpenSSOUtil._setCookieProperty(). This is where it fails, it tries to read the cookie with name [iPlanetDirectoryPro] from the liferay class com.liferay.util.CookieUtil using the HttpServletRequest object but all it get's a NULL. this value set's the authenticate status to false and hence the loop executes.
Following is the code from class com.liferay.util.CookieUtil
public static String get(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies;
String cookieName = GetterUtil.getString(cookie.getName());
if (cookieName.equalsIgnoreCase(name)) {
return cookie.getValue();
}
}
return null;
}
Can anyone please let me know why liferay is not able to find the cookie that opensso sent. If its related to Opensso setting about enable cookie value, then i have done that already which is here
In OpenSSO go to: Configuration -> Servers and Sites -> -> Security -> Cookie -> check Encode Cookie Value (set to Yes)
What works:
when this loop is executing i open another tab and login to my application explicitly, from my application when i signout it get's signout from opensso also. This is strange to me.
For more information, while this redirect loop happens, following URL's give me these set of information
http://opensso.ple.com:9090/openam/identity/getCookieNameForToken
string=iPlanetDirectoryPro
http://opensso.ple.com:9090/openam/identity/isTokenValid
boolean=true

Invalidate Session of a specific user

So for my webapp, if I remove a user that is currently logged in, and I want to invalidate his/her session. So that as soon as he/she refresh the page or navigate, they are no longer log in. The way I have now is that if a User logged in successfully, I will store the user object in my SessionScoped bean, and store the HttpSession to the Application Map. Below is my code
This is my SessionScoped bean
#PostConstruct
public void init() {
User user = UserDAO.findById(userId, password);
Map<String, Object> appMap = FacesContext.getCurrentInstance().
getExternalContext().getApplicationMap();
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().
getExternalContext().getSession(false);
appMap.put(userId, session);
}
Is this a correct approach? If so, how do I clean up my application map?
Is this a correct approach?
There are basically 2 ways.
Store the HttpSession handle in the application scope by the user ID as key so that you can get a handle of it and invalidate it. This may work for a small web application running on a single server, but may not work on a web application running on a cluster of servers, depending on its configuration.
I would only store it in another map in the application scope, not directly in the application scope like as you did, so that you can easier get an overview of all users and that you can guarantee that an arbitrary user ID won't clash with an existing application scoped managed bean name, for example.
Add a new boolean/bit column to some DB table associated with the user which is checked on every HTTP request. If the admin sets it to true, then the session associated with the request will be invalidated and the value in the DB will be set back to false.
how do I clean up my application map?
You could use HttpSessionListener#sessionDestroyed() for this. E.g.
public void sessionDestroyed(HttpSessionEvent event) {
User user = (User) event.getSession().getAttribute("user");
if (user != null) {
Map<User, HttpSession> logins = (Map<User, HttpSession>) event.getSession().getServletContext().getAttribute("logins");
logins.remove(user);
}
}
I think you can use your approach (with some modifications proposed by #BalusC) plus some notification mechanism (to make it work in distributed environment). You can do one of the following:
Use a topic queue subscribed by all your servers. When you remove user from your admin panel the JMS message will be created and sent to the topic. Every server will be responsible for invalidating the user session if it exists on the particular server (if the session is referenced in servletContext map).
Implement some action to invalidate the user session and run this action on every server in the cluster (The admin panel should send HTTP request to every server).
Use JGroups and TCP reliable multicast.
All of these solutions are not simple but much faster than polling the DB server on every request.

How to get user name in session bean when redirected to error page?

Can anyone tell me how to get user name in session bean correctly? In our application we do it by calling this method:
#Resource
private EJBContext _context;
private String getUserName() {
return _context.getCallerPrincipal().getName();
}
And this works fine till everything's fine. But when we get some 500 or 404 error and redirect user to corresponding page (which is set in web.xml) this method returns "WLS KERNEL" as user name. How to get correct user name in this case?

Resources