Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Аутентификация клиентов по Сертификатам


Автор: drG100k 14.10.2010, 00:03
Здравствуйте, столкнулся со связкой jdbc realm и <login-method>CLIENT-CERT</login-method> в одном флаконе.
Конечно всё написано в Java EE Tutorial, но, как всегда "размыто".
Вот, решил собрать всё важное в одном месте, в качестве напоминалки для себя  smile, а заодно может кому ещё пригодится.
Буду писать рецепт для связки 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 в браузер и тестим...
    

Автор: taro 19.10.2010, 09:33
Проект выложите, что ли? 
А то не очень понятно, за что вы боролись. 

Автор: 0x00 22.10.2010, 15:52
тоже хочется проект увидеть целиком

Автор: drG100k 23.10.2010, 14:02
Цитата(taro @ 19.10.2010,  09:33)
Проект выложите, что ли? 
А то не очень понятно, за что вы боролись.

Боролся за то чтобы методы в этих строчках  
Код

out.println(request.isUserInRole("loginRole") ? "Yes" : "No");          
out.println(request.getRemoteUser());

не возвращали всегда false и null  со всеми вытекающими... т.е. чтобы работали аннотация @RolesAllowed, метод SessionContext.isCallerInRole(String), EJBContext.getCallerPrincipal() и всякая прочая лабуда, относящаяся к разграничению прав в приложении

Весь проект из 2ух файлов web.xml и сервлет  smile они все выложены
Всё остальное относится к настройкам Tomcat


user posted image
вот такая схема аутентификации

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)