We are currently trying to implement a web application which is using the Java EE authentication mechanism with a FORM-based login, inside the Websphere 6.1 web container. If the authentication is successful, we have everything working; the LDAP membership groups are being retrieved, the group to role mapping is being performed, and the roles are being returned to and correctly interpreted by the web application.
If the authentication is not successful, the form-login-error page is being returned. However, this is a static page that simply says something like "there was an error". How do we trap the specific error that prevented the successful login (username/password incorrect, LDAP repository unavailable, account locked, password expired, etc.)? It seems like there should be some easy way to do this, as you would want to treat some "security" exceptions differently than others.
I use Struts so it will do forwarding for you. If you don't have a framework (why not?) you'll will have to do it manually.
The Java EE spec covers the j_security_check servlet.
The login page POSTs j_username and j_password to the j_security_check servlet. Your app will be configured to error to an unauthorized page (see web.xml) but will (initially) call a servlet. 401 or 403 will go to a forbidden page (again web.xml)
Inside that servlet (which extends the HttpServlet) - you will check for all that good stuff.
public final void doGet(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException, java.io.IOException
{
// initialize the app
AppInit initializer = new AppInit();
// get the logger
log = new Log4jWrapper(this.getClass());
// initialize the application session
HttpSession sess = request.getSession(true);
sess.setAttribute(CommonConstants.SESSION_CURR_USER_ID, request.getRemoteUser());
// initialize the JSP to forward to based on the user role
String fwdJSP = "SetupMainPage.jsp";
if (request.isUserInRole(CommonConstants.ROLE_MANAGER)) {
log.debug("User is a Manager");
}
//else other role checks - (these are users in groups in the LDAP)
// initialize the application session and set a variable to indicate that
// we are coming from a first time login (not a timeout login)
sess.setAttribute(CommonConstants.SESSION_COMING_FROM_INITIAL_LOGIN,"TRUE");
disp = getServletContext().getRequestDispatcher("SetupMainPage.jsp");
disp.forward(request, response);
}
//else failure
Unknown user
[11/22/08 8:54:47:993 EST] 7f6ac69c FormLoginServ E SECJ0118E: Authentication error during authentication for user s
right user - wrong password, but the request.getRemoteUser() will have a value
[11/22/08 8:56:45:082 EST] 7f51469c FormLoginServ E SECJ0118E: Authentication error during authentication for user jbsymolo
Unfortunately - i don't have any examples of someone locked out but I going to assume that the main security directory (LDAP) you will have an entry for the user for that.
This is from someone else (so I can't take credit)
I think this page describes how to do what you want to do.
Specifically how to retrieve the authentication exception from an arbitrary underlying authentication source (looks like Websphere calls them user registries).
Throwable t = com.ibm.websphere.security.auth.WSSubject.getRootLoginException();
if (t != null)
t = determineCause(t);
Where determineCause() is defined on the same page. This way, even if your server is configured to authenticate against a John Deer tractor, you will have access to the "OutOfGasLoginException" if there is one. The above code can go into the Servlet, Servlet Filter, or JSP that is redirect to by the container (as described above by jsymolon). It simply examines the exceptions and then places a corresponding friendly error message on the resulting page.
This is ancient knowledge - I believe to have done such a thing with tomcat. There was, as far as I can remember, no standard way, as the implementation was completely decoupled from the request and frontend web stuff, so that it was difficult to establish any means of communication between the authenticating component and the frontend (e.g. error page).
We ended up with a tomcat specific way, relying heavily on the current implementation. I'm no longer with that company, so I can't tell about the current state of the code or the solution we chose back then. I believe you'll also have to have some Websphere specific solution - be it the use of thread local variables, keying messages with the username that attempted to log in, somehow getting hold of the session identifier or similar.
Check this article Securing J2EE Applications with a Servlet Filter. I believe it covers your requirement to be able to pass the reason for the authentication error.
The JavaEE specification does not provide a standard mean to get an authentication feedback like error codes.
According to the following IBM Redpaper about z/OS security integration in gray note on page 57: IBM specific extension is available so that the error page JSP can report a specific message (like password expired) based on an error status code.
According to the WebSphere InfoCenter the FormLoginWeb sample from the TechSamp package in your WebSphere installation (samples/src/TechSamp/FormLoginWeb) is supposed to demonstrate such IBM specific extension but... The only thing interesting is the LoginFilter that intercepts calls on /j_security_check and is able to do pre-login validation and post-login action as explained in details in that paper.
With such a mechanism it is possible to get login exception from JAAS Subject and set an login error code in HttpSession so that the error page can generate a specific message.
Related
Please help me on this.
void setCreateSessionAllowed(boolean createSessionAllowed)
method of
org.springframework.security.web.savedrequest.HttpSessionRequestCache
class says
If true, indicates that it is permitted to store the target URL and exception information in a new HttpSession (the default). In situations where you do not wish to unnecessarily create HttpSessions - because the user agent will know the failed URL, such as with BASIC or Digest authentication - you may wish to set this property to false.
So I did not understand the description properly, also we are using a product and its documentation says setting it to false will disable the creation of anonymous user sessions.
So my question is, session creation and associating it with a request is servlet container's job. So How come using this method(setCreateSessionAllowed) will not create a session.
Please validate my understanding, is it correct or not. also
setCreateSessionAllowed(false), will JSESSIONID be created or not?
The HttpSessionRequestCache saves the last URL requested by the client in a user session.
This is used by spring security, when it redirects you to a login page, to restore the url after a successful login.
In case of basic or digest authentication is directly authenticated or asked to resend the request with the credentials. Therefore URL caching is not necessary.
If setCreateSessionAllowed is set to true, it will by default create a session to store the last url.
If set to false it will only support this feature if a session is already is created.
If no url is stored, spring security will use the default target URL supplied in the spring security configuration.
As for your last question, it is not directly obvious to me how it will affect the anonymous login feature of your mentions product.
I have a custom AuthenticationProvider with the authenticate method.
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
> Check username, password, throw exceptions where needed
return new CustomAuthenticationToken(username, grantedAuthorities);
}
And the token:
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken
{
public CustomAuthenticationToken(ICurrentUserContext currentUser, List<GrantedAuthority> authorities) {
super(currentUser.getUsername(), currentUser.getPassword(), authorities);
}
}
When I login with Chrome, Firefox, there is no problem whatsoever.
In IE 8/9 I have a very weird problem. Sometimes it will only call the method authenticate one time, it will login and everything works as expected. But from time to time, it will call authenticate twice, and fails to log in.
Does anybody have any clue?
I've tested it on Tomcat btw.
I've found the problem, with careful tracing the debug log of the Spring Security.. Hopefully this will help someone in the future.
Apparantly, spring security default migrates sessions after login. But in IE it does not migrate the authentication cookie to the new session, resulting in presenting of the login page.
The fix is easy, and can be done in the Spring Security xml:
<http use-expressions="true">
<!--
This settings is for IE. Default this setting is on migrateSession.
When IE tries to migrate the session, the auth cookie does not migrate,
resulting in a nice login screen again, after you've logged in.
This setting ensures that the session will not be invalidated, and thus IE will still work as expected.
-->
<session-management session-fixation-protection="none" />
</http>
Look at this please Internet Explorer buggy when accessing a custom weblogic provider.
Maybe you habe to disable cookies no your Tomcat
Migrating the session is entirely a server-side process and should be invisible to the browser. All it should see is a new Set-Cookie header for the JSESSIONID, which it should respect.
My best guess is that you are seeing this tomcat bug, which will cause different effects depending on how a browser interprets the duplicate headers. It was originally reported because of this issue with a Blackberry browser which is closely related to what you're seeing here.
But you don't say which versions of either Spring Security or Tomcat you are using (always a good idea :-)), so it's hard to say for sure.
Table of contents
Quick Reference
Spring Security Core plugin
<< 17IP Address Restrictions19Logout Handlers >>
18 Session Fixation Prevention - Reference Documentation
Authors: Burt Beckwith, Beverley Talbott
Version: 2.0-RC3
18 Session Fixation Prevention
To guard against session-fixation attacks set the useSessionFixationPrevention attribute to true:
grails.plugin.springsecurity.useSessionFixationPrevention = true
Upon successful authentication a new HTTP session is created and the previous session's attributes are copied into it. If you start your session by clicking a link that was generated by someone trying to hack your account, which contained an active session id, you are no longer sharing the previous session after login. You have your own session.
Session fixation is less of a problem now that Grails by default does not include jsessionid in URLs (see this JIRA issue), but it's still a good idea to use this feature.
Note that there is an issue when using the cookie-session plugin; see this issue for more details.
The table shows configuration options for session fixation.
Property Default Value Meaning
useSessionFixationPrevention true Whether to use session fixation prevention.
sessionFixationPrevention.migrate true Whether to copy the session attributes of the existing session to the new session after login.
sessionFixationPrevention.alwaysCreateSession false Whether to always create a session even if one did not exist at the start of the request.
http://grails-plugins.github.io/grails-spring-security-core/guide/sessionFixation.html
I'm using JEE6 security annotations #RolesAllowed("Admin") and a programmatic login:
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
request.login(loginUser.getUserName(), loginUser.getPassword());
However, for testing, I'd like to be able to 'simulate' a user login, and fool the app that a user logged in, without actually executing the above code. request.login(...) will ping the container for a login, I just want to tell the container that foo user has logged in and there is no need to check the password. I really don't want a solution that involves writing a login module or changing the settings of the entire container.
Thanks guys!
EDIT #1: What appserver am I using?
I'm using GlassFish 3.1.1. However, if possible, I'd like a solution that is 'container independent' using any available JEE6 api.
EDIT #2: Mr. Balus (a well regarded expert) is unaware of any api-neutral way bypassing the actual login. I supposed I could write my own "Yes-Man" login module, but I'm curious if anyone knows how to bypass the restriction specifically in the GlassFish environment.
Thanks!
EDIT #3: To anyone who finds this question later, I'm switched to Apache Shiro. I've used Spring Security in the past, but it's overkill for this app.
There's no API-provided facility for that. You could create a Filter which checks a certain VM argument or environment variable or even a JNDI variable and then does the login automagically.
E.g.
String login = System.getProperty("development.login");
if (login != null && request.getRemoteUser() == null) {
String[] nameAndPassword = login.split(":"); // Assuming "name:password".
request.login(nameAndPassword[0], nameAndPassword[1]);
}
I went through a similar question here. But I am yet not clear over concepts. Here is my scenario...
My client (a mobile device app) has a login screen to enter username, password. After submission, he should see the list of books in the database plus the list of books subscribed by that user.
I am having a /LoginService which accepts username, password & checks a mysql database for credential validation. Only after authorization....I have a /BookService ; GET on which returns all the books in database.
Should I use GET, POST or PUT on my loginservice ? Since a login request is a read-only operation, I should use GET - but this sounds stupid for browser(as the submitted data is visible).
What are accesstokens (mentioned in the linked answer above), and how to generate them using Java ? I am using Jersey for development. Are they a secure way of authorization ?
Thanks !
As far as I understand you are trying to implement stetefull communication between client and server. So you login with first request and then use some kind of token to make further requests.
Generally I can recommend you to have stateless communication. This means, that you authenticate and authorize each request. In this scenario you don't need LoginRestService. Important points here are:
Client can provide userName and password through HTTP Headers (non-standard, something like UserName: user and Password: secret).
At the server side you can use
Use AOP: just wrap you BooksService with AuthAdvice (which you should write yourself). In advise you access somehow (with Jersey functionality) HTTP request, take correspondent headers from it, authenticate and authorize user (that you load from DB), put user in ThreadLocal (so that it would be available to the rest of your app) if needed and just invoke correspondent method or throw exception if something wrong with credentials.
Use Jersey functionality: (sorry I'm not very familliar with Jersey, I'm using CXF, but conceptually it should be the same) just create some kind of AuthHendler and put it in request pre-processing pipeline. In this handler you need tho make exactly the same as in AuthAdvice
Now each of your request would be authenticated and authorized when it reaches BooksService. Generally stateless implementation is much better for scalability.
If you want to go statefull way, than you can just use HttpSession. LoginService.login() should be POST request because you actually making some side-effects at the server. Service will perform authentication of your user according to provided username and password and put loaded User object to session. At this point, the server side session is created and client has session ID in the cookies. So further requests should automatically send it to the server. In order to authorize requests to BooksService you still need some kind of Advice of Handler (see stateless solution). The only difference: this time user is taken from the HttpSession (you should check that you are logged in!).
Update: And use HTTPS! :)
I've got nothing to dispute in Easy Angel's answer, but got the impression you'd like some additional comment on the concepts too.
The problem is clearer if you think in terms of resources rather than services. Think of your proposed login as generating a new authorization resource, rather than querying a login service. Then you see that POST makes perfect sense.
The authorization token would be a key for your user into the session object (as explained in EA's answer). You'd probably want to generate it by concatenating some information that uniquely identifies that user and hashing it. I certainly agree that a stateless authentication method would be preferable, if you're aiming to get all the benefits of REST.
Use what is available in HTTP: HTTP AUTH over SSL.
Protect all your resources with HTTP AUTH and the browser will take care of providing a login for the user.
If you need session information on top of that, use cookies or a session parameter.
Cookies were made for exactly these kinds of purposes and usually work well.
I have an application deployed on WebLogic 10.3.2 (11g), in which the user logs in through SSO. In other words, if the user is not logged in, he is redirected to SSO, where he logs in, and then is redirected back to the application. The whole redirection takes place by an the Oracle HTTP Server (a modified apache), which makes sure that only SSO-authenticated users can see the applciation.
So, when the user finally sees the application, he is already logged in.
Is there a way to use Seam security with this scenario? What I would like is to use the roles of the Subject to restrict access to certain pages and components.
A way I thought of, but for which I am not sure, is to use the subject that is populated by the SSO authentication provider of WebLogic, and use it to populate the Identity component of Seam. That would take place in the authentication method, which will always return true (since the user is already logged in). Inside the method, the credentials and roles of the Subject will be "transfered" inside the Seam identity.
Is this feasible at all?
Cheers!
You could write your own authenticate method, or override the Identity class and the login() method to achieve this. I've done something similar with a reverse proxy that performed our authentication. In the scenario, the proxy sent back the user ID of the authenticated user and all the groups they were a member of as header values. I wrote a filter to intercept the headers and then used my custom Identity class to do the rest.