| « Tomcat and SSL redirection | Nothing to do with IT... » |
Tomcat LDAP authentication
Using Tomcat6 (Tomcat 6.0.13 to be precise
) on CentOS 4.5, a LDAP authentication can be set up using the JNDI Realm.
1/ Tomcat server configuration
Edit the configuration file and modify the $CATALINA_HOME/conf/server.xml tag to match the following:<Realm />
<Realm className="org.apache.catalina.realm.JNDIRealm" debug="99"
connectionName="uid=bindingproxyname,ou=profile,dc=domain,dc=com"
connectionPassword="bindingproxypasswd"
connectionURL="ldap://ldap.domain.com:389"
userPattern="uid={0},ou=people,dc=domain,dc=com"
roleBase="ou=group,dc=domain,dc=com"
roleName="cn"
roleSearch="memberUid={1}"
/>
The connectionName and connectionPassword are optionals. They stand for the binding credentials to access the LDAP.
The is the URL to access the LDAP server.connectionURL
The userPattern describes where users are stored in the LDAP.
The roleBase describes where roles (in my case, LDAP groups) are stored.
The roleName is the name of the attribute that contains the name of the role (group).
The roleSearch defines the LDAP filter to verify for searching roles.
In this case, members of a group are defined by the memberUid attribute in each group cn. So to get the list of roles a user belongs to, the filter must get only roles which has the user defined as a member (memberUid=username).
2/ Tomcat application configuration
But… that was only the first part of authentication.
...
Now, if you want to secure a tomcat application, asking your users to authenticate, you’ll also have to modify the $CATALINA_HOME/webapps/yourapplication/WEB-INF/web.xml file of your application. I have added the following to the file:
<security-constraint>
<display-name>Security Constraint</display-name>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<!-- Define the context-relative URL(s) to be protected -->
<url-pattern>/*</url-pattern>
<!-- If you list http methods, only those methods are protected -->
</web-resource-collection>
<auth-constraint>
<!-- Anyone with one of the listed roles may access this area -->
<role-name>source</role-name>
</auth-constraint>
</security-constraint>
<!-- Default login configuration uses form-based authentication -->
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Form-Based Authentication Area</realm-name>
<form-login-config>
<form-login-page>/security/protected/index.jsp</form-login-page>
<form-error-page>/security/protected/error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- Security roles referenced by this web application -->
<security-role>
<role-name>source</role-name>
</security-role>
This means that any page of the yourapplication application requires to be authenticated to be displayed.
The authentication is done using the /security/protected/index.jsp
Moreover, people need to be member of the source role (in my case, in the source ldap group) to be granted the access.
The /security/protected/index.jsp file contains the following html form:
<form method="POST" action='<%= response.encodeURL("j_security_check") %>' >
<table cellpadding="2" border="0" cellspacing="0">
<tr>
<td align="right">Username:</td>
<td align="left"><input type="text" name="j_username" size="9"></td>
</tr>
<tr>
<td align="right">Password:</td>
<td align="left"><input type="password" name="j_password" size="9"></td>
</tr>
<tr>
<td align="right"><input type="submit" value="Log In"></td>
<td align="left"><input type="reset"></td>
</tr>
</table>
</form>
This should be enough!!! Enjoy it!
29 comments
Thanks for this well explained blog. I was wondering how tomcat would check the password without specifying any information about where to find the passwords in the LDAP drectory?
I tried using the above configuration, but I cannot get the user to get authenticated.
Well, make sure that you've replaced the generic parameters in the <Realm /> definition by some that match your ldap server.
You're right about the password. I've been using Sun Directory as a LDAP server. I've never had to mention a specific field for the password...
Thanks for your comment. Do not hesitate to come back to me with more information ;-)
Once logged in, the user name can be fetch using the following:
<%= request.getRemoteUser() %>
I'm using this to display a "Welcome username" message on the tomcat app pages.
Hope this helps!
We are migrating our applicaton from Tomcat5 to tomcat-6.0.18. In our applicaton we are verifying users with LDAP. But when we tried to login we are getting following error:
HTTP Status 403 - Access to the requested resource has been denied.
Can someine please help us.
Thanks.
JNDIRealm[Catalina]: dn=uid=ravindra.paliwal,ou=Equant ,ou=People,o=globalone.net
JNDIRealm[Catalina]: validating credentials by binding as the user
JNDIRealm[Catalina]: binding as uid=ravindra.paliwal,ou=Equant ,ou=People,o=globalone.net
JNDIRealm[Catalina]: Exception performing authentication
javax.naming.ServiceUnavailableException: cod.dc.iad.equant.com:389; socket closed; remaining name ''
at com.sun.jndi.ldap.Connection.readReply(Connection.java:410)
at com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:340)
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:193)
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2640)
at com.sun.jndi.ldap.LdapCtx.ensureOpen(LdapCtx.java:2549)
at com.sun.jndi.ldap.LdapCtx.ensureOpen(LdapCtx.java:2523)
at com.sun.jndi.ldap.LdapCtx.doSearch(LdapCtx.java:1904)
at com.sun.jndi.ldap.LdapCtx.doSearchOnce(LdapCtx.java:1896)
at com.sun.jndi.ldap.LdapCtx.c_getAttributes(LdapCtx.java:1289)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:213)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:121)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:109)
at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:121)
at org.apache.catalina.realm.JNDIRealm.bindAsUser(JNDIRealm.java:1231)
at org.apache.catalina.realm.JNDIRealm.checkCredentials(JNDIRealm.java:1122)
at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:868)
at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:782)
at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:229)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:446)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
at org.apache.catalina.authenticator.SingleSignOn.invoke(SingleSignOn.java:417)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:793)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:702)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:571)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:644)
at java.lang.Thread.run(Thread.java:534)
JNDIRealm[Catalina]: Closing directory context
What kind of error do you have?
Do you have any logs?
You mean that you have to enter your login, and whatever login you enter, you just get the Forbidden status page?
Did I miss something?
Otherwise, make sure that you have access to the index.jsp page...
Try adding
allRolesMode="authOnly"
inside the realm-tag.
-timo-
allRolesMode="authOnly" fixed all my problems
thanks for providing such a great tutorial. I followed your tutorial, and getting work some how. After authenticating is success then how to redirect to the success page? And in success page I want to display the user name and his roles from LDAP. So can you suggest me the code snippet to get the roles of an authenticated user and redirecting to the sucess page.
First, thank you for your kind comment...
Well, let try to get a little more technical now...
It's been a long time since I've done this tomcat thing, but as far as I remember, the success page is simply the
index.jsp page from your webapp:$CATALINA_HOME/webapps/yourapplication/index.jsp
In order to display the user name, you can use the following in your index.jsp page:
<%= request.getRemoteUser() %>
I'm not sure there are other built-in functions you can use to access LDAP information about the logged in user. I guess that you have to implement an LDAP request to get whatever LDAP field you want and display it in your webapp, once you have the user name...
Hope this helps...
I've managed to get the index.jsp page to show, and I believe it is hitting the LDAP server okay - if you use an invalid password it spits you to error.jsp like it should - but when you hit "okay" with a valid password it gives a 403 error. Do you have any idea what might be the cause for this? As far as I know I simply followed your instructions to the letter.
I can post my web.xml, server.xml, etc if needs be. I hope you are still around, and I hope you can help me!
Thank you.
Newie
Do I need to change the pairs ou=people, dc=domain, uid=(0) to the equivalentes in my LDAP server. I mean both sides (Strings) of the comparison? Or maybe just the right side value of the comparison? Are the keywords uid, ou, dc, cn, memberUid standard keywords for all posible configurations? And by configurations also mean not only in Tomcat, but also Spring Security, etc. I know this is an exhaustive question, I am having problems finding documntation. Thank you for the tutorial.
The entry userPattern="uid={0},ou=people,dc=domain,dc=com" should match your LDAP server.
In my case, users are stored in LDAP (I'm running Sun Directory Server here - but I could have been OpenLDAP or any other one) following this schema:
The user name is known by the uid.
Users entries are stored in the "people" ou, for the "domain.com" domain.
uid, ou, dc, cn, ... are quite standard, but the LDAP schema may vary depending on the LDAP server you're using...
Well, that's a really short answer to your long question, but anyone who wants to add more is welcome! ;-)
what do uid={0} and memberUid={1} mean? I a mean the numbers between curly brackets. Great tutorial.
James
Thankyou for the article and the help comments. I have got the following info from an apache server. I have change the domain, group and company names. I do not know how to map this info to the tomcat configuration.
AuthLdapUrl ldap://ldap02.domain.com:389/o=company,c=com?uid?sub?(objectClass=*)
Require ldap-group cn=thegroup,o=company,c=com
roleSearch="roleOccupant={1}"
Does that work? because the roleOccupant is a DN and not a UID so I don't think it works.
For folks who are asking about j_security_check, in my case it points nowhere. The idea is that when a restricted resource is requested, tomcat will redirect to the login page. On successful login, the page is redirected to restricted page which was originally requested. On failed login, it redirects to error.jsp.
My requirement is plain - authenticate all users regardless of their group. So I got rid of the roles. Finally my web.xml looks like this
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.sample.jnditry1.hello</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/restricted/hello</url-pattern>
</servlet-mapping>
<security-constraint>
<display-name>Security Constraint</display-name>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<!-- Define the context-relative URL(s) to be protected -->
<url-pattern>/restricted/*</url-pattern>
<!-- If you list http methods, only those methods are protected -->
</web-resource-collection>
<auth-constraint>
<!-- Anyone with one of the listed roles may access this area -->
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
As for the servlet hello -
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
resp.getWriter().write("GET username=" + req.getUserPrincipal().getName());
}
Finally my request is HTTP GET - http://localhost:8080/jnditry1/restricted/hello
which asks for login and once I do shows my username. Hope that helps.
I've found this article very useful for my project, I just have one question.
Is it possible to select x users from a single role to gain access?
For example, my AD have a role named "users" with the user "u1","u2" and "u3".
And after i define the role name:
<role-name>users</role-name>
I just want u1 and u3 to gain access.
Thanks!
How can I tell tomcat to populate http headers with info from the ldap record, eg. first name, last name, user name.
The authorization header is there, but what I really want is a header with the remote user in it, eg. the username I logged in with.
Thank you for this very good example.
- Mika