Русский English Тэги View Sergey Zolotaryov's profile on LinkedIn Вход
Настройка JAAS в Tomcat
Постоянная ссылка 16-07-2008 anydoby java

Однажды, когда я был еще маленьким, я решил разобраться с JAAS. Судя по вступительной документации, это было очень несложно, а главное, очень удобно, так как можно было описать весь процесс авторизации/аутентификации в одном модуле и использовать его в разных приложениях. На практике оказалось, что все не совсем так просто и удобно. А главное, документации по этой теме совсем мало.

Тогда я это дело забросил, потому как показалось слишком сложно, а сегодня возникла необходимость снова возникла и, скажу вам, разобраться оказалось несложно. Итак, начнем.

Основной интерфейс, который нужно будет реализовать - javax.security.auth.spi.LoginModule.


package com.anydoby.security;


import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import com.anydoby.security.realm.Role;
import com.anydoby.security.realm.User;

/**
 * 
 * @author SergeyZ
 * 
 */
public class MyLoginModule implements LoginModule {

  private Subject subject;
  private CallbackHandler callbackHandler;

  private String password;
  private String name;


  public boolean abort() throws LoginException {
    return true;
  }


  public boolean commit() throws LoginException {
    authenticate();
    populateRoles();
    return true;
  }


  private void populateRoles() {
    // add user's roles to subject being authenticated
    subject.getPrincipals().add(new Role("user"));
  }


  private void authenticate() {
    // TODO check if user and password are valid
    subject.getPrincipals().add(new User(name));
  }


  public void initialize(Subject subject, CallbackHandler callbackHandler,
      Map<String, ?> sharedState, Map<String, ?> options) {
    this.subject = subject;
    this.callbackHandler = callbackHandler;
  }


  public boolean login() throws LoginException {
    NameCallback name = new NameCallback("User name");
    PasswordCallback password = new PasswordCallback("Password", true);
    try {
      this.callbackHandler.handle(new Callback[] { name, password });
      this.name = name.getName();
      this.password = new String(password.getPassword());
    } catch (Exception e) {
      throw new LoginException(e.getMessage());
    }

    if (isBlank(this.name)) {
      throw new CredentialNotFoundException("User name is required");
    }
    if (isBlank(this.password)) {
      throw new CredentialNotFoundException("Password is required");
    }

    return true;
  }


  public boolean logout() throws LoginException {
    return true;
  }

}

Разберемся, что здесь происходит. Первым вызывается метод initialize(). В нем можно сохранить ссылку на еще пустой объект авторизации пользователя и колбэки, которые служат для получения данных от пользователя - ведь ввести имя и пароль можно не только в веб форму, но и в терминале telnet и т.д. и т.п.

Далее вызывается метод login(). В нем у нас уже есть имя и пароль от пользователя. Их извлекаем при помощи известных Tomcat колбэков - NameCallback и PasswordCallback. Все.

Потом вызывается метод commit(), котором удобно разместить логику собственно авторизации/аутентификации - определение, правильно ли введено имя пользователя и пароль, а также наполнение subjectа именем и ролями. Имя пользователя и роли в Java это объекты типа java.security.Principal. Чтобы отличить имя от роли, мы создали два класса: User:


package com.anydoby.security.realm;


import java.security.Principal;

public class User implements Principal {

  private final String name;


  public User(String name) {
    this.name = name;
  }


  public String getName() {
    return name;
  }

}

и Role:


package com.anydoby.security.realm;


import java.security.Principal;

public class Role implements Principal {

  private final String name;


  public Role(String name) {
    this.name = name;
  }


  public String getName() {
    return name;
  }

}

чтобы Tomcat мог отделить роли и имена, нужно указать это в конфигурации контекста или хоста (как кому удобнее):


<Context path="/securitytest" reloadable="true">
    <Realm className="org.apache.catalina.realm.JAASRealm" 
        appName="securitytest" 
        userClassNames="com.anydoby.security.realm.User" 
        roleClassNames="com.anydoby.security.realm.Role" debug="99" />
</Context>

В userClassNames и roleClassNames можно поместить список классов (один или несколько через запятую), которые Tomcat будет воспринимать в как имя пользователя или роль. appName должен совпадать с именем кофигурации JAAS модуля. Если использовать стандартную конфигурацию от Sun, то нужно указать файл с конфигурацией в системном свойстве java.security.auth.login.config.

Сначала создаем файл конфигурации, например в папке TOMCAT_HOME/conf/jaas.config:


securitytest {
com.anydoby.security.MyLoginModule REQUIRED debug=true;
};

Теперь нужно этот файл скормить Tomcatу. Сделать это просто - в TOMCAT_HOME/bin/catalina.bat (или catalina.sh) добавить строку:


CATALINA_OPTS="-Djava.security.auth.login.config=%CATALINA_HOME%/conf/jaas.config"

или для Unix:


CATALINA_OPTS="-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config"

Вот и все. Да, не забудьте положить jar файл с классом модуля и User/Role в TOMCAT_HOME/lib

Добавить комментарий

memphis04
10-08-2009

Заинтересовало описание. Можно ли показать jsp файлы к этому описанию, web.xml и TLD если они есть?

java-jsp
22-02-2010

Интересное решение. Жаль, что нет реализующих его непосредственно jsp-файлов. Их можно как-то посмотреть? Спасибо.

anydoby
28-07-2010

Да тут, собсно, стандартный какой login.jsp сойдет, чтобы форму показать :) это ж про JAAS статься.

i.kyb
23-04-2014

А куда это посовывать?

Предыдущая статья Луч света в царстве JSF Следующая статья Глюки проксей в Hibernate