context: - JSF on glassfish 3.1
- FORM based authentication (realm via a derby database)
- I am using no servlets but JSF
(1) on requesting the base URL index.html is loaded which includes a link to /protected/welcome.xhtml
(2) click on that link => due to the login requirement (see web.xml below) login.html is loaded
(3) user fills in credentials and submits the form (to a UserBean)
(4) welcome.xhtml is loaded successfully upon successful login
(5) user logs out from welcome.xhtml via button and is redirected to index.html
Problem: The logout is not working correctly:
(a) When after the logout the login button is pressed again but without providing credentials then welcome.xhtml again is loaded (without any user information). That shouldn't be possible. It cannot be the cache (tested by deactivating the cache in the browser).
(b) It does not delete the session (session cookie not deleted on the client and keeps the same value).
Hi, I just checked my code and I see that I've commented out the call to session.invalidate() and
replaced it with this:
I really can't recollect why I did this and unfortunately at that time had not put into place a proper
changelog & version control so the reason is lost, but perhaps you could try this alternative approach.
Container-managed security does not route the login process through application code. The login page is displayed by the server directly, and the server reads the returned login form and processes it. At no time is the application even aware that the login took place, and in particular, the FacesServlet is never invoked. That's why they call it container managed security.
So there's no way you could be populating JSF beans from data on the login page.
An IDE is no substitute for an Intelligent Developer.
Joined: May 12, 2009
>So there's no way you could be populating JSF beans from data on the login page.
Let's say you've built a mail application, you login with container security and don't populate
any backing beans with data from the login page, whose mail are you going to display? I'm
not sure I get your point, you've got to be able to know who has logged in surely.
Container-managed security supports Single Signon. Or, in other words, if the proper Realm was configured on the server, a user who had logged into a completely different application written in a completely different language on a completely different server would never see your login screen, because SSO is "login once, run everywhere". For example, OpenID, where I can login to google and not have to login again to SmartSheets. JSF container-managed security supports SSO from the get-go; no application modifications or custom code required. In fact, even the config information in web.xml doesn't change.
But aside from that, it's a dicey security practice to attempt to do any sort of business logic in the middle of the login process anyway. It introduces the possibility that someone could exploit access to an object before security was fully established.
On the other hand, you CAN tell the userID quite easily, although not directly from JSF. When the container sets up a security context either when it logs you in or when it checks with SSO and obtains confirmation, a UserPrincipal object is constructed and used as an anchor for your application security. This object can be read from the HttpServletRequest object. You can also get the userId directly from the HttpServletRequest.getRemoteUser method. I normally create a special JSF Utilities class to hold methods that need to root around under the FacesContext in order to keep JSF-specific code out of my POJOs, and one of the methods I generally provde is a "getUserId" method.
Since the container maintains security, not the applications, there is, in fact, the only way a container-secured app can actually detect that a login ever happened. If the previous request had a null userPrincipal/remoteUser, and the current request has non-null values, someone, somewhere, somehow logged into the security system between the first request and the dispatching of the second request.
Joined: May 12, 2009
I think we're singing off the same hymn sheet here. My welcome file is a secure page so
the user would need to be authenticated to access it. This has a preRenderView which
calls getRemoteUser() and display data for the relevant user. I hope this is ok because
I don't see any other way of doing it.
Back to the OP: was there a problem with my suggestion to call logout()? if so, what
are you supposed to do to logout. I'm pretty sure I researched this and this was the
recommended thing to do.
You should be OK on the PreRenderView. The security system kicks in when the protected URL is requested (PLEASE NOTE - URL, not RESOURCE!).
If the user fails login, the URL request is discarded and nothing ever reaches the app at all. However, if the user validates (logs in), then the URL will be processed without further interference, and the security context (UserPrincipal and remoteUserId) will have been initialized before the application code is called.
One thing I did notice was the comment about Portlets. Each Portlet is a specialized webapp and Portlets most definitely want SSO, because the alternative would be to force a login screen for every stinkin' portlet on the portal webpage. Likewise, the idea of logging out of a single portlet is meaningless. It's an all-or-nothing deal.
Joined: Jul 26, 2011
That leaves me with the initial questions. In the meantime, I checked again my app with the following results:
1.) Start of the application (index.html with the link to a CMS-protected area)
Result: no cookie yet set, and hence no session established
2.) Klick on the link to the protected area. As a result not the welcome.xhtml is loaded but login.html (declarative security)
Result: a cookie is set, in the specific test with value 50033e41d18d500e302036f6eb74
3.) Enter credentials and submit form; welcome.xhtml of the protected area is loaded
Result: a new session is established (I assume that is the security session) with cookie value 500736ddf603308a1e58f032b08e
4.) Logout again. session.invalidate() is called. Again index.html is loaded.
Result: Still the same cookie is set on the client: 500736ddf603308a1e58f032b08e
5.) Click again on the link to the protected area
Result: welcome.xhtml is opened but without information from the backing bean and a new session is established with cookie value 502f6e71e4ca1580e9761cf8d983
- After step 4 I would have assumed the cookie to be deleted. A possible explanation for that behaviour could be that the server is deleting the session only on the server but not on the client. That means the cookie is still available on the client but without a corresponding session on the server. Is that understanding correct? If not, why is the session still existant, although session.invalidate was called?
- What can be done to avoid after step 5 the loading of welcome.xhtml? What would be a "good" way to solve that?
- Where can I find more info/readings/books about the mentioned security context (UserPrincipal and remoteUserId) - to get deeper into the subject?
I never really paid attention to when/how security cookies are deleted. Deletion of the secured session on the server is what counts. After all, Bad People could simply replay a logged-out cookie otherwise!
The login page isn't really a part of the application process. If/when needed, the container will present it and process it, but it's all transparent to the webapp. All the webapp really knows is that when that protected URL is processed, security has been established.
As I mentioned earlier, container-managed security applies itself to URLs, and not to resources. This can be a problem in JSF, since the URL often lags the resource. It's what the "redirect" directive on your logout action is supposed to be addressing.
Keep a close eye on the URLs as you progress - especially the ones on forms and hyperlinks. They're what's probably causing the anomaly. Use the "View Page" source option on your browser if you need to check the generated URL.
As far as documentation on the User ID and UserPrincipal go, all I ever had was the J2EE JavaDocs. The user ID is simply the login ID. UserPrincipal is mostly stuff that apps can't use, except for the user ID property. Its main virtue is that it's present when you're secure and absent (null) otherwise.
Joined: Jul 26, 2011
I'm not sure whether I do understand you correctly:
- with respect to the cookies you can't really say because you didn't focus on it in the past.
- with respect to the protected page being loaded you can recommend to check whether the links (URLs) are correct? (they are)
Is that what you mean? Because as of now I miss a hint on what the problem could be and how I could solve it. An "anomaly" is either a bug or a feature. If it is a feature then something is wrong in my app. If it's a bug then someone might already know about it - since my application is not very sophisticated.
Well, the cookies aren't important, because, as I said, the server is in charge of what it will do with a session ID.
The URLs to check are what's actually being submitted, not what's coded, since there can be a difference between the 2, especially when you present a page from JSF.
Joined: Jul 26, 2011
@Brendan: thanks for your advice. Much appreciated!
@Tim: thanks for your rather general comments on JSF.
So currently it seems logging out is only possible with application managed security, i.e. it needs to be programmed within the application.
I am highly sceptical about that last assertion. None of the apps I write have run into that problem. The closest thing I know of is in when you use BASIC authentication rather than form-based authentication, where the only way to logout is to shutdown the browser.
I have switched identities by logging out via session.invalidate.