User Tools

Site Tools


java_security

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

java_security [2007/12/24 13:54]
java_security [2019/06/12 16:08] (current)
Line 1: Line 1:
 +[[start|На главную]]
  
 +====== Реализация системы безопасности в Java ======
 +  Всё нижеописанное исходит из предположения что в Sun хорошо подумали когда
 +  проектировали пермишены,​ секьюрити-менеджер и всё с ними связанное.
 +
 +
 +===== О чём это вообще =====
 +В типичном веб-приложении далеко не каждый пользователь может менять любые данные. У кого-то есть доступ только на чтение,​ кто-то может менять только свои данные,​ кто-то только данные пользователей из определённой группы или находящиеся в определённом состоянии. Возникает задача разграничивания прав. Есть несколько вариантов её решения. Вариант первый - логика разрешения/​запрещения жёстко задаётся в коде. Существует жёстко заданный набор групп с наборами правам. Этот подход работает только до тех пор пока не возникает необходимости изменить эти наборы. Другой подход связан с динамическим созданием групп и динамическим же назначением им прав. И логика по проверке просто проверяет,​ есть ли у текущего пользователя право на то действие,​ которое он пытается совершить.
 +==== Что защищать ====
 +Любые данные.
 +==== Зачем защищать ====
 +Чтобы разделять,​ кому что можно, а кому что нельзя делать.
 +
 +
 +===== Аутентификация =====
 +==== Несортированный трэш ====
 +<code java>
 +    //​Authorization init
 +    Policy.setPolicy(new Policy()
 +    {
 +      public boolean implies(final ProtectionDomain domain, final Permission permission)
 +      {
 +        for (final Principal principal : domain.getPrincipals()) {
 +          if (principal instanceof MyPrincipal) {
 +            //​получить откуда-то коллекцию прав принципала и вызвать для них implies, передав в качестве аргумента проверяемый пермишен.
 +          }
 +        }
 +        return false;
 +      }
 +    });
 +    //​Authentication init
 +    final Configuration orig = Configuration.getConfiguration();​
 +    Configuration.setConfiguration(new Configuration()
 +    {
 +      public AppConfigurationEntry[] getAppConfigurationEntry(final String name)
 +      {
 +        return (name.equals("​MY_APP"​)) ?
 +            new AppConfigurationEntry[]{
 +                new AppConfigurationEntry(
 +                    "​com.myApp.security.MyLoginModule",​ //наша реализация LoginModule.
 +                    AppConfigurationEntry.LoginModuleControlFlag.REQUISITE,​
 +                    Collections.<​String,​ Object>​emptyMap()
 +                )
 +            } : orig.getAppConfigurationEntry(name);​
 +      }
 +
 +      public void refresh()
 +      {
 +        orig.refresh();​
 +      }
 +    });
 +    System.setSecurityManager(new SecurityManager());​
 +    //​Аутентификация
 +    final String name = "​login";​
 +    final String password = "​password";​
 +    final LoginContext ctx = new LoginContext("​MY_APP",​ new CallbackHandler()
 +    {
 +      public void handle(final Callback[] callbacks) throws IOException,​ UnsupportedCallbackException
 +      {
 +        for (final Callback cb : callbacks)
 +        {
 +          if (cb instanceof NameCallback)
 +          {
 +            ((NameCallback) cb).setName(name);​
 +          }
 +          else if (cb instanceof PasswordCallback)
 +          {
 +            ((PasswordCallback) cb).setPassword(password.toCharArray());​
 +          }
 +          else
 +          {
 +            throw new UnsupportedCallbackException(cb);​
 +          }
 +        }
 +      }
 +    });
 +    ctx.login();​
 +    //​выполнение кода в контексте аутентифицированного пользователя
 +    Subject.doAs(ctx.getSubject(),​ new PrivilegedAction<​Object>​()
 +    {
 +      public Object run()
 +      {
 +        //Do something
 +        return null;
 +      }
 +    });
 +    ctx.logout();​
 +</​code>​
 +
 +===== Авторизация =====
 +
 +==== Краткое описание проверки пермишена ====
 +В Java существует [[http://​java.sun.com/​j2se/​1.5.0/​docs/​guide/​security/​spec/​security-specTOC.fm.html|встроенный механизм прав]].
 +
 +  * Проверяется,​ не равен ли ''​null'''​у ''​System#​getSecurityManager()''​.
 +    * Если равен, то считается что действие разрешено делать.
 +  * Вызывается [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​lang/​SecurityManager.html#​checkPermission(java.security.Permission)|SecurityManager#​checkPermission(java.security.Permission)]]
 +  * Берётся текущий ''​AccessControlContext''​ и вызывается у него метод [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​AccessControlContext.html#​checkPermission(java.security.Permission)|AccessControlContext#​checkPermission(java.security.Permission)]]
 +  * ''​AccessControlContext''​ итерейтится по содержащимся в нём ''​ProtectionDomain'''​ам и у них вызывается метод [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​ProtectionDomain.html#​implies(java.security.Permission)|ProtectionDomain#​implies(java.security.Permission)]]
 +  * Берётся текущая ''​Policy'',​ у которой вызывается метод [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​Policy.html#​implies(java.security.ProtectionDomain,​ java.security.Permission)|Policy#​implies(java.security.ProtectionDomain,​ java.security.Permission)]] с данным ''​ProtectionDomain''​ом.
 +
 +==== Компоненты системы авторизации ====
 +
 +  * [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​lang/​SecurityManager.html|SecurityManager]] - просто фасад над всей системой прав.
 +  * [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​AccessControlContext.html|AccessControlContext]] - контекст с информацией о том, в каких ''​ProtectionDomain'''​ах выполняется сейчас код.
 +  * [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​ProtectionDomain.html|ProtectionDomain]] - содержит информацию об URL'​ах (возможно с сертификатами),​ из которых получен выполняющийся код, ''​ClassLoader'',​ через который этот код был получен и (**внимание**) коллекцию [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​Principal.html|Principal]]'​ов,​ от имени которых код выполняется.
 +  * [[http://​java.sun.com/​j2se/​1.5.0/​docs/​api/​java/​security/​Policy.html|Policy]] - хранилище пермишенов,​ которое определяет какие права у кого есть.
 +
 +
 +==== Внедрение своих правил авторизации ====
 +
 +
 +=== Principal ===
 +
 +Скорее всего у вас в приложении есть класс ''​User''​. Нужно будет написать реализацию ''​Principal'''​а для него (Внимание! Не надо делать так чтобы ваш ''​User''​ реализовывал ''​Principal'''​а,​ т.к. отсутствие юзера не означает отсутствие ''​Principal'''​а).
 +
 +== Примерная реализация ==
 +<code java>
 +public class UserPrincipal implements java.security.Principal {
 +  // ------------------------------ FIELDS ------------------------------
 +  /**
 +   * Константа для незарегистрированного пользователя.
 +   */
 +  private static final UserPrincipal nullUserPrincipal = new UserPrincipal (null);
 +  private final User user;
 +
 +  // -------------------------- STATIC METHODS --------------------------
 +
 +  public static UserPrincipal getPrincipal (final User user) {
 +    return user == null ? nullUserPrincipal : new UserPrincipal (user);
 +  }
 +
 +  // --------------------------- CONSTRUCTORS ---------------------------
 +
 +  protected UserPrincipal (final User user) {
 +    super ();
 +    this.user = user;
 +  }
 +
 +  // ------------------------ CANONICAL METHODS ------------------------
 +
 +  public boolean equals (final Object obj) {
 +    if (obj == null || !(obj instanceof UserPrincipal)) {
 +      return false;
 +    }
 +    final UserPrincipal o = (UserPrincipal) obj;
 +    final User myUser = this.getUser ();
 +    final User oUser = o.getUser ();
 +    if (myUser == null && oUser == null) {
 +      return true;
 +    } else if (myUser != null && oUser != null) {
 +      return myUser.equals (oUser);
 +    } else {
 +      return false;
 +    }
 +  }
 +
 +  public int hashCode () {
 +    return this.getClass ().hashCode () + (this.user == null ? 13 : this.user.hashCode ());
 +  }
 +
 +  // ------------------------ INTERFACE METHODS ------------------------
 +
 +  // --------------------- Interface Nullable ---------------------
 +
 +  public boolean isNull () {
 +    return this.user == null;
 +  }
 +
 +  // --------------------- Interface Principal ---------------------
 +
 +  public String getName () {
 +    return this.getClass ().getName () + " " + (this.user == null ? "​unregistered"​ : this.getUser ().getUserId ());
 +  }
 +
 +  // -------------------------- OTHER METHODS --------------------------
 +
 +  public User getUser () {
 +    return this.user;
 +  }
 +}
 +</​code>​
 +
 +
 +=== Policy ===
 +''​Policy''​ - это как раз то, куда надо всовывать свой код, чтобы делать проверку прав по своей базе, а не по стандартной реализации в policy-файле. Делается это просто - пишется свой класс, наследующийся от ''​Policy''​ (внимание,​ если в вашем приложении права могут динамически изменяться,​ то надо переопределять метод ''​implies'',​ т.к. в стандартной реализации он активно занимается кэшированием пермишенов.
 +
 +> **ВНИМАНИЕ** Считается,​ что право разрешено,​ если оно разрешено для **ВСЕХ** ProtectionDomain'​ов,​ находящихся в ''​AccessControlContext'''​е вызывающего кода.
 +
 +
 +
 +=== Собираем вместе ===
 +
 +== Установка Policy ==
 +<code java>
 +Policy.setPolicy (new MyPolicy ());
 +</​code>​
 +
 +== Включение SecurityManager'​а ==
 +<code java>
 +if (System.getSecurityManager () != null) {
 +  System.setSecurityManager (new SecurityManager ());
 +}
 +</​code>​
 +
 +== Вызов защищённого кода ==
 +<code java>
 +try {
 +  final Subject subject = ...;
 +  final T result = Subject.doAs (subject, new PrivilegedExceptionAction<​T>​() {
 +    public Object run () throws IOException,​ ServletException {
 +      //​выполнить какой-то код, защищённый правами.
 +    }
 +  });
 +} catch (PrivilegedActionException e) {
 +  ...
 +}
 +</​code>​
 +
 +== SecurityUtils ==
 +Пара методов для удобства.
 +
 +<code java>
 +public final class SecurityUtils {
 +  public static boolean hasPermission (final Permission permission) {
 +    try {
 +      checkPermission (permission);​
 +      return true;
 +    } catch (final SecurityException e) {
 +      return false;
 +    }
 +  }
 +
 +  public static void checkPermission (final Permission permission) throws SecurityException {
 +    if (System.getSecurityManager () != null) {
 +      System.getSecurityManager ().checkPermission (permission);​
 +    }
 +  }
 +}
 +</​code>​
 +
 +
 +===== Альтернативы =====
 +  * [[http://​acegisecurity.org/​|Acegi Security]]
 +FIXME
 +
 +===== Permissions Evangelizm =====
 +FIXME
 +
 +====== JAAS vs Acegi Security ======
 +
 +===== Java Security (JAAS) =====
 +{{jsf-ru:​plus.gif}} Является стандартом,​ поддерживается большим количеством технологий (EJB, Servlets, etc)
 +
 +{{jsf-ru:​plus.gif}} Простой API
 +
 +{{jsf-ru:​plus.gif}} Хорошо документирован
 +
 +{{jsf-ru:​minus.gif}} Довольно скудная базовая функциональность
 +
 +{{jsf-ru:​minus.gif}} Ограниченное встроенное кэширование (не предполагает изменения набора прав в процессе работы приложения),​ в случае отключения которого необходимо реализовывать кэширование на уровне приложения
 +
 +{{jsf-ru:​minus.gif}} Отсутствуют теги для проверки прав к различным элементам интерфейса (однако возможно их написание)
 +
 +{{jsf-ru:​minus.gif}} Политики принятия решений необходимо реализовывать вручную
 +
 +{{jsf-ru:​minus.gif}} В некоторых случаях контейнеро-зависим (однако возможно написание контейнеро-независимого кода)
 +
 +
 +
 +===== Acegi Security =====
 +
 +{{jsf-ru:​plus.gif}} Хорошо документирован
 +
 +{{jsf-ru:​plus.gif}} Простая интеграция со Spring
 +
 +{{jsf-ru:​plus.gif}} Встроенные аудиты
 +
 +{{jsf-ru:​plus.gif}} Встроенные интерцепторы (например,​ для удаления из коллекций недоступных объектов)
 +
 +{{jsf-ru:​plus.gif}} Различные политики принятия решений (если все полиси разрешают,​ если хоть одна полиси разрешает,​ на основе голосования и очков)
 +
 +{{jsf-ru:​plus.gif}} Встроенные теги для проверки прав к различным элементам интерфейса (правда примитивные)
 +
 +{{jsf-ru:​plus.gif}} Встроенное кэширование
 +
 +{{jsf-ru:​plus.gif}} Контейнеро-независим
 +
 +{{jsf-ru:​plus.gif}} Встроенная поддержка опции «запомнить меня»
 +
 +{{jsf-ru:​plus.gif}} Множество адаптеров для различных механизмов аутентификации
 +
 +{{jsf-ru:​minus.gif}} Многократно более сложный API
 +
 +{{jsf-ru:​minus.gif}} Не специфицирован
 +
 +При этом следует учитывать,​ что имеется возможность оборачивать использование сторонним кодом JAAS’а в обращения к Acegi и наоборот. Так же необходимо понимать,​ что права виртуальной машины (доступ к файлам,​ сокетам,​ работа с класслоадерами и т.д. проверяются через JAAS, поэтому при использовании Acegi в любом случае будет необходимо направлять в него обращения к JAAS).