User Tools

Site Tools


java_security

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
java_security [2007/04/10 10:39] slonopotamusjava_security [2019/06/12 16:08] (current) – external edit 127.0.0.1
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).
java_security.txt · Last modified: 2019/06/12 16:08 by 127.0.0.1