Добавление полномочий в AuthenticationSuccessHandler

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

Когда я только начинал, я сопоставлял полномочия группы пользователей 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) в коллекции типа неприменим для аргументов (GrantedAuthority)

И код CustomAuthority:

public class CustomAuthority implements GrantedAuthority
{
    private String name;

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

    public String getAuthority() {
        return name;
    }
}

Любая помощь приветствуется.

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


person JDiPierro    schedule 12.07.2013    source источник


Ответы (2)


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

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

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

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

Достоинства: Думаю код подойдет в нужное место.

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

person Maksym Demidas    schedule 15.07.2013

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

Один из вариантов — использовать LdapAuthenticationProvider и LdapAuthoritiesPopulator , как описано в часто задаваемые вопросы. Вот пример из 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, что должно дать вам представление о том, как реализовать логику.

person Rob Winch    schedule 12.07.2013
comment
Я нашел способ сделать это в SuccessHandler, но это кажется правильным способом сделать это. Спасибо, что указали мне на это. - person JDiPierro; 15.07.2013
comment
На самом деле... похоже, что невозможно использовать LdapAuthoritiesPopulator с ActiveDirectoryLdapAuthenticationProvider? - person JDiPierro; 15.07.2013
comment
Вы правы (в моем ответе указано использовать LdapAuthenticationProvider, если вы хотите использовать LdapAuthoritiesPopulator). В качестве альтернативы, если вы хотите использовать ActiveDirectoryLdapAuthenticationProvider, используйте UserDetailsContextMapper. - person Rob Winch; 15.07.2013