2013-07-12 6 views
2

Я работаю с веб-сайтом Spring MVC и добавляю аутентификацию в Active Directory через LDAP. Компания не хочет использовать полномочия AD для сопоставления разрешений для веб-сайта, у нас есть база данных, в которой перечислены права каждого пользователя, поэтому я пытаюсь подключиться к ней, получить разрешения и добавить их в токен аутентификации пользователя.Добавление полномочий в AuthenticationSuccessHandler

Когда я только начал, я сопоставлял полномочия группы пользователей AD с GrantedAuthoritiesMapper, и у меня было это работает. Это выглядело так:

public class ActiveDirectoryGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper { 

    private static final String ROLE_ADMIN = "adminUserGroup"; 

    public ActiveDirectoryGrantedAuthoritiesMapper() 
    { } 

    public Collection<? extends GrantedAuthority> mapAuthorities(
      final Collection<? extends GrantedAuthority> authorities) 
    { 

     Set<CustomAuthority> roles = EnumSet.noneOf(CustomAuthority.class); 

     for (GrantedAuthority authority : authorities) 
     { 
      if (ROLE_ADMIN.equals(authority.getAuthority())) 
      { 
       roles.add(CustomAuthority.ROLE_ADMIN); 
      } 
      //Default role for all users. 
      roles.add(CustomAuthority.ROLE_EMPLOYEE); 
     } 
     return roles; 
    } 
} 

Теперь я пытаюсь преобразовать его, чтобы запросить нашу базу данных для получения разрешений. Я отошел от GrantedAuthoritiesMapper, чтобы сделать это по двум причинам. Во-первых, я не использую полномочия из LDAP, поэтому зачем их перехватывать? А также потому, что я не мог понять, как получить имя пользователя, регистрирующегося внутри GrantedAuthoritiesMapper. Я пытался использовать SecurityContext, но он давал мне NullPointerException всякий раз, когда я пытался позвонить context.getAuthentication().getName(). Я предполагаю, что пользователь еще не был полностью аутентифицирован.

Поэтому я перешел на использование AuthenticationSuccessHandler. Я пытался придерживаться логики примерно так же. Я пытаюсь добавить роли в токен аутентификации пользователя с authentication.getAuthorities().add(...);, но я получаю ошибки, которые мой CustomAuthority не распространяется GrantedAuthority. Он не расширяет его, но реализует интерфейс. Мне было интересно, было ли это потому, что это было перечисление, поэтому я изменил его на класс, и я все еще получаю ошибку. Вот код для пользовательских AuthenticationSuccessHandler как у меня сейчас:

public class CustomAuthoritiesMapper implements AuthenticationSuccessHandler 
{ 
    private CustomPermissionDAO permissionsDao = new CustomPermissionDAO(); 

    private static final String ROLE_ADMIN = "ADMIN_ACCOUNT"; 

    @Override 
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) 
      throws IOException, ServletException 
    { 
     List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>(); 

     List<DatabasePermission> permissionsForUser = permissionsDao.getPermissionByUsername(authentication.getName()); 

     for (DatabasePermission permission : permissionsForUser) 
     { 
      if (ROLE_ADMIN.equals(permission.getTag())) 
      { 
       roles.add(new CustomAuthority("ROLE_ADMIN")); 
      } 
      //Default role for all users. 
      roles.add(new DashboardAuthority("ROLE_EMPLOYEE")); 
     } 
     for(GrantedAuthority auth : roles) 
     { 
      authentication.getAuthorities().add(auth); 
     } 
    } 
} 

Я пробовал только о каждой комбинации все, что я могу думать .. Я изменил List<GrantedAuthority> к списку объектов CustomAuthority. Я попытался использовать addAll(roles) вместо добавления отдельных. Каждый раз, когда я получаю некоторое изменение этой же ошибки:

Метод add (capture # 1-of? Extends GrantedAuthority) в типе Collection не применим для аргументы (GrantedAuthority)

И код CustomAuthority:

public class CustomAuthority implements GrantedAuthority 
{ 
    private String name; 

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

    public String getAuthority() { 
     return name; 
    } 
} 

Любая помощь будет высоко ценится.

От взгляда на «связанные вопросы» это похоже на authentication.getName() может не работать здесь, но я хочу выяснить, почему я не могу добавить разрешения, которые я хочу добавить в полномочия пользователя, прежде чем я займусь этим вопрос.

ответ

2

Это не ответ на ваш вопрос. Это предложение о том, как этот coud быть выполнен по-другому.

Что для меня необычно, что вы используете AuthenticationSuccessHandler для вещей, которые должны быть сделаны AuthenticationManager и AuthenticationProviders. Представьте себе два AuthenticationProviders, один для LDAP и один для DB. Проблема в том, что если вы объедините их по умолчанию AuthenticationManager, тогда будет использоваться только первый провайдер. Вы можете подготовить пользовательские AuthenticationManager с несколько иной логикой:

  • Он знает о двух провайдерах
  • Он называет их в правильном порядке
  • Он сочетает в себе два результата аутентификации от двух поставщиков в один глобальный результат, принимая пользователь учетные данные от LDAP и полномочия от БД.

Недостатки: новый разработчик, желающий добавить третьего поставщика аутентификации, может быть удивлен пользовательским AuthenticationManager.

Преимущества: Я думаю, что код будет подходящим в нужном месте.

Надеюсь, это поможет.

1

Причина, по которой вы не можете получить текущее имя пользователя из SecurityContext, состоит в том, что она еще не заполнена (вы все еще работаете над выяснением, какие роли включить в нее).

Один из вариантов - использовать LdapAuthenticationProvider и LdapAuthoritiesPopulator as described in the FAQ. Вот пример из часто задаваемых вопросов

public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator { 
    @Autowired 
    JdbcTemplate template; 

    public List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) { 
     List<GrantedAuthority> = template.query("select role from roles where username = ?", 
                new String[] {username}, 
                new RowMapper<GrantedAuthority>() { 
      /** 
      * We're assuming here that you're using the standard convention of using the role 
      * prefix "ROLE_" to mark attributes which are supported by Spring Security's RoleVoter. 
      */ 
      public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { 
       return new GrantedAuthorityImpl("ROLE_" + rs.getString(1)); 
      } 
     }); 
    } 
} 

Это должно сработать для вас, так как оно передает имя пользователя для вас. Используйте это имя пользователя для запроса, чтобы получить роли для пользователя.

Как указано в FAQ, вам необходимо подключить его к LdapAuthenticationProvider с помощью настраиваемого LdapAuthoritiesPopulator.

Другой вариант - ввести пользовательский UserDetailsContextMapper. По умолчанию используется LdapUserDetailsMapper, который должен дать вам представление о том, как реализовать логику.

+0

Я нашел способ взлома, чтобы сделать это в SuccessHandler, но это действительно правильный способ сделать это. Спасибо, что указал на это. – JDiPierro

+0

На самом деле .. похоже, что использовать LdapAuthoritiesPopulator с помощью ActiveDirectoryLdapAuthenticationProvider невозможно? – JDiPierro

+0

Вы правы (в моем ответе указывается использование LdapAuthenticationProvider, если вы хотите использовать LdapAuthoritiesPopulator). Кроме того, если вы хотите использовать ActiveDirectoryLdapAuthenticationProvider, используйте UserDetailsContextMapper. –

Смежные вопросы