Setting up client-cert authentication with roles on Tomcat 6.0
I'm setting up a web application that uses client certificate authentication. For right now, I've just set up a test user account and a 'user' role to test authentication and authorization. I'm using a DataSourceRealm and a postgreSQL database configured as such:
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/postgres" localDataSource="true"
userTable="users" userNameCol="username" userCredCol="password"
userRoleTable="user_roles" roleNameCol="role" />
<Resource auth="Container" type="javax.sql.DataSource" name="jdbc/postgres"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/test"
maxActive="100" maxIdle="30" maxWait="-1"
username="test" password="test" />
When I first set up the database and application I used FORM authentication with the following configuration in web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>Test</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-failed.jsp</form-error-page>
</form-login-config>
</login-config>
The database contained the user: testuser and assigned the role: user. Login with FORM authentication worked as expected.
I then changed the FORM authentication method to CLIENT-CERT and set up the necessary certificates, CA, etc. The connector in server.xml is configured like this:
<Connector SSLEnabled="true" clientAuth="true"
maxThreads="150" port="8443" protocol="HTTP/1.1" scheme="https"
keystoreFile="C:\Workspace\Test\keystore.jks"
keystorePass="changeit"
truststoreFile="C:\Workspace\Test\truststore.jks"
truststorePass="changeit"
secure="true" sslProtocol="TLS"/>
The user is identified in the database as "CN=testuser,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU"
If I leave the auth-constraint开发者_如何学Python specifying the "user" role I get a 403 permission denied error accessing any page. It looks like authentication succeeds but it cannot determine the user's role from the database. If I comment out the auth-constraint, authentication succeeds and I can access the protected page.
It looks like it cannot look up the user's role in the database though the only thing in the db I changed was how the username is represented. After authentication the following code:
X500Principal p = certs[0].getSubjectX500Principal();
out.println (p.getName());
produces "CN=testuser,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU" which is the value stored in the username column in the database which is also mapped to the 'user' role in the user_roles table.
test=> select * from users;
username | password
-----------------------------------------------------------+----------
CN=testuser,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU |
(1 row)
test=> select * from user_roles;
username | role
-----------------------------------------------------------+------
CN=testuser,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU | user
(1 row)
What else should I be doing to be able to lookup the user roles? From everything I've seen so far it looks like this should work. Thanks for any help.
I found the answer to my problem.
Even though
X500Principal p = certs[0].getSubjectX500Principal();
out.println (p.getName());
returns the subject DN as "CN=testuser,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU", turning on verbose logging on the DB shows "CN=testuser, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU" (with inserted spaces between the fields) being submitted to the database in the query.
LOG: ... execute <unnamed>: SELECT null FROM users WHERE username = $1
parameters: $1 = 'CN=testuser, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU'
LOG: ... execute <unnamed>: SELECT role FROM user_roles WHERE username = $1
parameters: $1 = 'CN=testuser, O=Internet Widgits Pty Ltd, ST=Some-State, C=AU'
It seems X509Principal.getName()
returns getName(X500Principal.RFC2253)
where tomcat seems to be using getName(X500Principal.RFC1779)
. Updating the DB to use the RFC 1779 format solved my problem.
精彩评论