Здравствуйте, столкнулся со связкой jdbc realm и <login-method>CLIENT-CERT</login-method> в одном флаконе.
Конечно всё написано в Java EE Tutorial, но, как всегда "размыто".
Вот, решил собрать всё важное в одном месте, в качестве напоминалки для себя

, а заодно может кому ещё пригодится.
Буду писать рецепт для связки Tomcat 6 + Postgresql + Java EE 5/6 Web Application
Для начала нужно создать таблицы в базе данных
Код |
create table users (user_name varchar(200) not null primary key, passwd char(1) default null); create table roles(user_name varchar(200) not null references users(user_name), role_name varchar(100) not null, primary key(user_name,role_name));
|
поле passwd нам не понадобится так как пользователи будут аутентифицироваться по сертификатам.
Название полей user_name в обеих таблицах лучше сделать одинаковыми.
В Tomcat ${CATALINA-BASE}/conf/server.xml для SSL соединений прописываем
Код |
... <Connector port="443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" keystoreFile="${user.home}/.keystore" keystorePass="SuperSecretPassword" truststoreFile="${user.home}/.truststore" truststorePass="SuperSecretPassword" clientAuth="true" sslProtocol="TLS" /> ...
|
Для JDBC Realm
Код |
<Realm className="org.apache.catalina.realm.JDBCRealm" driverName="org.postgresql.Driver" connectionURL="jdbc:postgresql://localhost:5432/mydb" username="postgresusername" password="puPassword" userTable="users" userNameCol="user_name" userCredCol="passwd" userRoleTable="roles" roleNameCol="role_name"/>
|
Пользователю postgresusername желательно дать права только на чтение.
Для тестов сгенерим пару сертификатов
для сервера
Код |
keytool -genkeypair -alias tomcat-server -keyalg RSA -keysize 1024 -keystore .keystore
|
Для клиента (в формате, который понимает, например браузер FireFox)
Код |
keytool -genkeypair -alias client -keyalg RSA -keysize 1024 -keystore client.p12 -storetype PKCS12
|
На вопросы keytool ответил так: CN="User#1", OU=Tester, O=Mega Corp, L=SPb, ST=NW, C=RU - эта строка и будет user_name
Так как мы негенерили "левый" сертификат, импортируем его в truststore Сервера
Код |
keytool -exportcert -keystore client.p12 -storetype PKCS12 -file client.cer -alias client keytool -importcert -file client.cer -keystore .truststore
|
на вопрос доверяем?, отвечаем огга
Всё, естесственно, из Tomcat ${user.home}
После этого можно запустить Tomcat и посмотреть в лог, если не сыпятся exception по поводу соединения с базой и отсутствия файлов .keystore, .truststore, то смело переходим к Web Application
Для простоты пусть будет сервлет
Дескриптор web.xml
Код |
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <display-name>Certificate Servlet Auth</display-name> <servlet-name>login</servlet-name> <servlet-class>com.org.whatever.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <security-constraint> <web-resource-collection> <web-resource-name>loginPage</web-resource-name> <url-pattern>/login</url-pattern> <!-- Все методы --> <http-method-omission></http-method-omission> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> <auth-constraint> <role-name>loginRole</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>CLIENT-CERT</auth-method> </login-config> <security-role> <role-name>loginRole</role-name> </security-role> </web-app>
|
Ну и сам сервлет
Код |
package com.org.whatever;
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
/** * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = null; try { out = response.getWriter(); out.println("<html>"); out.println("<body>"); out.println("<head>"); out.println("<title>Cert login</title>"); out.println("</head>"); out.println("<body bgcolor=\"white\">"); out.println("<h3> Client logged in</h3>"); out.println("<table border=0><tr><td>"); out.println("method:"); out.println("</td><td>"); out.println(request.getMethod()); out.println("</td></tr><tr><td>"); out.println("url"); out.println("</td><td>"); out.println(request.getRequestURL()); out.println("</td></tr><tr><td>"); out.println("protocol"); out.println("</td><td>"); out.println(request.getProtocol()); out.println("</td></tr><tr><td>"); out.println("Is user in loginRole?"); out.println("</td><td><b>"); out.println(request.isUserInRole("loginRole") ? "Yes" : "No"); out.println("</b></td></tr><tr><td>"); out.println("remote User"); out.println("</td><td>"); out.println(request.getRemoteUser()); out.println("</td></tr><tr><td>"); String cipherSuite = (String) request.getAttribute("javax.servlet.request.cipher_suite"); if (cipherSuite != null) { out.println("SSLCipherSuite:"); out.println("</td><td>"); out.println(cipherSuite); out.println("</td></tr><tr><td>"); out.println("Session Id"); out.println("</td><td>"); out.println(request.getAttribute("javax.servlet.request.ssl_session")); out.println("</td></tr>"); } out.println("</table>"); out.println("</body>"); out.println("</html>"); } finally { if (out != null) { out.close(); } } } /** * Handles the HTTP <code>GET</code> method. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
/** * Handles the HTTP <code>POST</code> method. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
/** * Returns a short description of the servlet. * @return a String containing servlet description */ @Override public String getServletInfo() { return "Login Servlet"; } }
|
Осталось забить в базу данных user_name = 'CN="User#1", OU=Tester, O=Mega Corp, L=SPb, ST=NW, C=RU'
role_name = loginRole для этого юзверя
Импортируем client.p12 в браузер и тестим...