Grails + spring-security-core: как назначить роль после входа пользователя в систему?

У меня есть приложение Grails, использующее spring-security-core и spring-security-ldap с аутентификацией в Active Directory. У меня есть пользовательские UserDetails и UserDetailsContextMapper.

У меня есть вариант использования, когда пользователь может временно взять на себя дополнительные обязанности для одного сеанса. Я хотел бы иметь возможность назначить роль пользователю после, когда он уже вошел в систему (т.е. в середине сеанса), а затем удалить эту роль, когда сеанс истечет.

У меня есть таблица базы данных user_role, в которой хранятся отношения между пользователями и ролями. В этом случае не имеет значения, добавляется ли отношение в базу данных, а затем удаляется по истечении срока действия сеанса или просто существует только в памяти. В любом случае, я ищу способ назначить роль (и немедленно применить ее) в тот момент, когда пользователь вошел в систему. Возможно ли это?


person Ben    schedule 28.09.2011    source источник


Ответы (2)


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

ie:

public class MyUserDetails implements UserDetails {
    // holds the authorities granted to the user in DB
    private List<GrantedAuthority> authorities;
    // holds the temporarily added authorities for the mid-session
    private List<GrantedAuthority> extraAuthorities;

   public GrantedAuthority[] getAuthorities() {
       List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
       auths.addAll(this.authorities);
       auths.addAll(this.extraAuthorities);
       return auths;
   }

   public void addExtraAuthorities(GrantedAuthority...auths) {
       for (GrantedAuthority a : auths) {
           this.extraAuthorities.add(a);
       }
   }

   public void clearExtraAuthorities() {
       this.extraAuthorities.clear();
   }
}

Или вы можете создать оболочку вокруг исходных UserDetails, которая будет содержать дополнительные полномочия, обернуть их и установить для текущего объекта аутентификации в начале середины сеанса и развернуть в конце середины сеанса.


Изменить:

Как указал Бен в комментарии, UserDetails.getAuthorities() вызывается только один раз, при входе в систему, когда создается объект Authentication - я забыл об этом. Но это подводит нас к правильному ответу, и на этот раз я уверен, что это сработает, потому что я сам сделал это таким образом. Authentication.getAuthorities() - это метод, на который следует обратить внимание, а не UserDetails.getAuthorities(). И я действительно рекомендую для этого оболочку, а не пользовательскую реализацию Authentication интерфейса, поскольку она будет более гибкой и позволит прозрачно поддерживать различные механизмы аутентификации внизу.

ie:

public class MidSessionAuthenticationWrapper implements Authentication {

    private Authentication wrapped;
    private List<GrantedAuthority> authorities;

    public MidSessionAuthenticationWrapper(
            Authentication wrapped, Collection<GrantedAuthority> extraAuths) {
        this.wrapped = wrapped;
        this.authorities = new ArrayList<GrantedAuthority>(wrapped.getAuthorities());
        this.authorities.addAll(extraAuths);
    }

    public Authentication getWrapped() {
        return this.wrapped;
    }

    @Override
    public Collection<GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    // delegate all the other methods of Authentication interace to this.wrapped
}

Тогда все, что Вам нужно сделать, это начать в середине сеанса:

Authentication authentication = 
    SecurityContextHolder.getContext().getAuthentication();
Collection<GrantedAuthority> extraAuths = 
    null; // calculate the extra authorities
MidSessionAuthenticationWrapper midSessionAuthentication =
    new MidSessionAuthenticationWrapper(authentication, extraAuths);
SecurityContextHolder.getContext().setAuthentication(midSessionAuthentication);

И в середине сеанса:

MidSessionAuthenticationWrapper midSessionAuthentication =
    (MidSessionAuthenticationWrapper) SecurityContextHolder.getContext().getAuthentication();
Authentication authentication = midSessionAuthentication.getWrapped();
SecurityContextHolder.getContext().setAuthentication(authentication);
person Roadrunner    schedule 29.09.2011
comment
Спасибо, Roadrunner. Я попробовал ваш подход, однако оказалось, что getAuthorities() не вызывается, когда происходит проверка безопасности, либо с использованием ‹sec:ifAnyGranted›, либо аннотации контроллера. Насколько я могу судить, этот метод вызывается только при первом входе пользователя в систему. Отличается ли это от вашего опыта? - person Ben; 29.09.2011
comment
@ Бен Ты прав. Я исправил свой ответ, чтобы отразить это. Теперь я вспомнил, почему в прошлый раз сам менял Authentication, а не UserDetails :). Извините, что сначала ввел Вас в заблуждение. - person Roadrunner; 01.10.2011
comment
Это работает отлично, спасибо! (Извините за задержку с ответом, я немного отвлекся). - person Ben; 06.10.2011

У меня была такая же проблема, и я нашел более короткое решение. Я использую «до»-фильтр, чтобы проверить, есть ли у пользователя LDAP локальный (db) пользователь, найти или создать его, если необходимо, добавить роли пользователю, а затем повторно аутентифицировать пользователя через:

springSecurityService.reauthenticate(user.username)

После этого пользователь будет иметь текущий список полномочий.

person Jörg Rech    schedule 11.09.2013
comment
Это отлично сработало для меня. Назначьте новую роль пользователю, повторите аутентификацию и готово! - person Sergio del Amo; 18.10.2014
comment
Можете ли вы добавить ссылку на учебник или документацию, как именно это сделать? Например. с перехватчиками Grails 3.x. Спасибо - person Mexx; 04.04.2016