Реализация области Tomcat с аутентификацией LDAP и авторизацией JDBC

Я работаю в устаревшей среде, где сервер LDAP используется только для аутентификации и не содержит ролей, а авторизация выполняется для базы данных, которая содержит сопоставление ролей пользователя, но без паролей.

мой план заключается в реализации новой области Tomcat путем расширения JNDIRealm и переопределения методов роли для вызова инкапсулированного JDBCRealm.

моя область объявлена в server.XML-код:

<Realm className="com.example.LdapJdbcRealm"
   connectionURL="ldap://ldaphost:389"
   resourceName="LDAP Auth"
   userPattern="uid={0}, ou=Portal, dc=example, dc=com"
   dbConnectionURL="jdbc:oracle:thin:@oracledb:1521:dbname"
   userTable="db_user" userNameCol="user_id"
   userRoleTable="db_user_role_xref" roleNameCol="role_id" />

Это сочетание стандартных имена свойств для JNDIRealm & JDBCRealm с небольшим изменением, поскольку они оба используют connectionURL.

package com.example;

import org.apache.catalina.Realm;
import org.apache.catalina.Context;
import org.apache.catalina.deploy.SecurityConstraint;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.realm.JNDIRealm;
import org.apache.catalina.realm.JDBCRealm;

import java.security.Principal;
import java.io.IOException;

public class LdapJdbcRealm extends JNDIRealm implements Realm
{
    private JDBCRealm jdbcRealm = new JDBCRealm();

    protected static final String info = "com.example.LdapJdbcRealm/1.0";
    protected static final String name = "LdapJdbcRealm";

    public String getDbConnectionURL() {
        return jdbcRealm.getConnectionURL();
    }

    public void setDbConnectionURL(String dbConnectionURL) {
        jdbcRealm.setConnectionURL(dbConnectionURL);
    }

    public String getUserTable() {
        return jdbcRealm.getUserTable();
    }

    public void setUserTable(String userTable) {
        jdbcRealm.setUserTable(userTable);
    }

    public String getUserNameCol() {
        return jdbcRealm.getUserNameCol();
    }

    public void setUserNameCol(String userNameCol) {
        jdbcRealm.setUserNameCol(userNameCol);
    }

    public String getUserRoleTable() {
        return jdbcRealm.getUserRoleTable();
    }

    public void setUserRoleTable(String userRoleTable) {
        jdbcRealm.setUserRoleTable(userRoleTable);
    }

    public String getRoleNameCol() {
        return jdbcRealm.getRoleNameCol();
    }

    public void setRoleNameCol(String roleNameCol) {
        jdbcRealm.setRoleNameCol(roleNameCol);
    }

    public boolean hasResourcePermission(Request request,
                                         Response response,
                                         SecurityConstraint[]constraints,
                                         Context context) throws IOException
    {
        return jdbcRealm.hasResourcePermission(request, response, constraints, context);
    }

    public boolean hasRole(Principal principal, String role) {
        return jdbcRealm.hasRole(principal, role);
    }
}

это в основном работает, авторизация возвращает Принципала из LDAP, который не имеет ролей, как ожидалось. Тот же Принципал входит hasResourcePermission() и терпит неудачу, потому что в нем нет требуемых ролей. Очевидно, я упускаю какой-то важный код.

Я ищу решения. Я мог бы попробовать расширить jdbcrealm и добавить аутентификацию LDAP, но это кажется больше работа.

Я также считаю, что эта аутентификация LDAP/авторизация DB не является необычным шаблоном. Есть ли уже альтернативное решение?

Это не под моим контролем добавить роли в LDAP или пароли к БД, так что это не решения для меня.

2 ответов


вы не указали версию Tomcat, которую вы используете, поэтому я собираюсь с 6.x здесь.

похоже, вы передаете hasResourcePermission в JDBC, оставляя оба findSecurityConstraints и hasUserDataPermission в руки в JNDI. Вы должны делегировать их все или ни одного.

обновление: JNDIRealm звонки protected getRoles(DirContext, User) как часть его authenticate() метод. Вам нужно переопределить, что и направить его по JDBCRealm getRoles().


Я все еще получаю электронные письма по этому вопросу с регулярной частотой, поэтому вот конечный продукт для всех.


LdapJdbcRealm.java

package org.apache.catalina.realm;

import org.apache.catalina.Realm;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.SecurityConstraint;

import javax.naming.directory.DirContext;
import java.io.IOException;
import java.security.Principal;
import java.util.List;

/**
 * LdapJdbcRealm is a minimal implementation of a <b>Realm</b> to connect to LDAP
 * for authentication and a database for authorization.<br>
 * <br>
 * Example server.xml configuration fragment:<br>
 * <pre>
   &lt;Realm className="org.apache.catalina.realm.LdapJdbcRealm"
      connectionURL="ldap://ldaphost:389"
      resourceName="LDAP Auth" driverName="oracle.jdbc.driver.OracleDriver"
      userPattern="uid={0}, ou=Portal, dc=example, dc=com"
      dbConnectionName="dbuser" dbConnectionPassword="dbpassword"
      dbConnectionURL="jdbc:oracle:thin:@oracledb:1521:dbname"
      userTable="users" userNameCol="user_id"
      userRoleTable="user_role_xref" roleNameCol="role_id" /&gt;
 * </pre>
 *
 * @author Greg Chabala
 *
 * Created by IntelliJ IDEA.
 * User: gchabala
 * Date: Jul 14, 2009
 * Time: 4:56:37 PM
 */
public class LdapJdbcRealm extends JNDIRealm implements Realm
{
    /**
     * Encapsulated <b>JDBCRealm</b> to do role lookups
     */
    private JDBCRealm jdbcRealm = new JDBCRealm();

    /**
     * Descriptive information about this <b>Realm</b> implementation.
     */
    protected static final String info = "org.apache.catalina.realm.LdapJdbcRealm/1.0";

    /**
     * Descriptive information about this <b>Realm</b> implementation.
     */
    protected static final String name = "LdapJdbcRealm";

    /**
     * Set the all roles mode.
     *
     * @param allRolesMode authentication mode
     */
    public void setAllRolesMode(String allRolesMode) {
        super.setAllRolesMode(allRolesMode);
        jdbcRealm.setAllRolesMode(allRolesMode);
    }

    /**
     * Return the username to use to connect to the database.
     *
     * @return username
     * @see JDBCRealm#getConnectionName()
     */
    public String getDbConnectionName() {
        return jdbcRealm.getConnectionName();
    }

    /**
     * Set the username to use to connect to the database.
     *
     * @param dbConnectionName username
     * @see JDBCRealm#setConnectionName(String)
     */
    public void setDbConnectionName(String dbConnectionName) {
        jdbcRealm.setConnectionName(dbConnectionName);
    }

    /**
     * Return the password to use to connect to the database.
     *
     * @return password
     * @see JDBCRealm#getConnectionPassword()
     */
    public String getDbConnectionPassword() {
        return jdbcRealm.getConnectionPassword();
    }

    /**
     * Set the password to use to connect to the database.
     *
     * @param dbConnectionPassword password
     * @see JDBCRealm#setConnectionPassword(String)
     */
    public void setDbConnectionPassword(String dbConnectionPassword) {
        jdbcRealm.setConnectionPassword(dbConnectionPassword);
    }

    /**
     * Return the URL to use to connect to the database.
     *
     * @return database connection URL
     * @see JDBCRealm#getConnectionURL()
     */
    public String getDbConnectionURL() {
        return jdbcRealm.getConnectionURL();
    }

    /**
     * Set the URL to use to connect to the database.
     *
     * @param dbConnectionURL The new connection URL
     * @see JDBCRealm#setConnectionURL(String)
     */
    public void setDbConnectionURL(String dbConnectionURL) {
        jdbcRealm.setConnectionURL(dbConnectionURL);
    }

    /**
     * Return the JDBC driver that will be used.
     *
     * @return driver classname
     * @see JDBCRealm#getDriverName()
     */
    public String getDriverName() {
        return jdbcRealm.getDriverName();
    }

    /**
     * Set the JDBC driver that will be used.
     *
     * @param driverName The driver name
     * @see JDBCRealm#setDriverName(String)
     */
    public void setDriverName(String driverName) {
        jdbcRealm.setDriverName(driverName);
    }

    /**
     * Return the table that holds user data..
     *
     * @return table name
     * @see JDBCRealm#getUserTable()
     */
    public String getUserTable() {
        return jdbcRealm.getUserTable();
    }

    /**
     * Set the table that holds user data.
     *
     * @param userTable The table name
     * @see JDBCRealm#setUserTable(String)
     */
    public void setUserTable(String userTable) {
        jdbcRealm.setUserTable(userTable);
    }

    /**
     * Return the column in the user table that holds the user's name.
     *
     * @return username database column name
     * @see JDBCRealm#getUserNameCol()
     */
    public String getUserNameCol() {
        return jdbcRealm.getUserNameCol();
    }

    /**
     * Set the column in the user table that holds the user's name.
     *
     * @param userNameCol The column name
     * @see JDBCRealm#setUserNameCol(String)
     */
    public void setUserNameCol(String userNameCol) {
        jdbcRealm.setUserNameCol(userNameCol);
    }

    /**
     * Return the table that holds the relation between user's and roles.
     *
     * @return user role database table name
     * @see JDBCRealm#getUserRoleTable()
     */
    public String getUserRoleTable() {
        return jdbcRealm.getUserRoleTable();
    }

    /**
     * Set the table that holds the relation between user's and roles.
     *
     * @param userRoleTable The table name
     * @see JDBCRealm#setUserRoleTable(String)
     */
    public void setUserRoleTable(String userRoleTable) {
        jdbcRealm.setUserRoleTable(userRoleTable);
    }

    /**
     * Return the column in the user role table that names a role.
     *
     * @return role column name
     * @see JDBCRealm#getRoleNameCol()
     */
    public String getRoleNameCol() {
        return jdbcRealm.getRoleNameCol();
    }

    /**
     * Set the column in the user role table that names a role.
     *
     * @param roleNameCol The column name
     * @see JDBCRealm#setRoleNameCol(String)
     */
    public void setRoleNameCol(String roleNameCol) {
        jdbcRealm.setRoleNameCol(roleNameCol);
    }

    @Override
    public SecurityConstraint[] findSecurityConstraints(Request request, Context context)
    {
        return jdbcRealm.findSecurityConstraints(request, context);
    }

    @Override
    public boolean hasUserDataPermission(Request request, Response response,
                                         SecurityConstraint []constraints) throws IOException
    {
        return jdbcRealm.hasUserDataPermission(request, response, constraints);
    }

    @Override
    public boolean hasResourcePermission(Request request, Response response,
                                         SecurityConstraint[]constraints,
                                         Context context) throws IOException
    {
        return jdbcRealm.hasResourcePermission(request, response, constraints, context);
    }

    @Override
    public boolean hasRole(Principal principal, String role) {
        return jdbcRealm.hasRole(principal, role);
    }

    /**
     * Return a List of roles associated with the given User. If no roles
     * are associated with this user, a zero-length List is returned.
     *
     * @param context unused. JDBC does not need this field.
     * @param user The User to be checked
     * @return list of role names
     *
     * @see JNDIRealm#getRoles(DirContext, User)
     * @see JDBCRealm#getRoles(String) 
     */
    @Override
    protected List<String> getRoles(DirContext context, User user)
    {
        return jdbcRealm.getRoles(user.username);
    }
}