4

我有一个使用 spring-security-core 和 spring-security-ldap 的 grails 应用程序,并针对 Active Directory 进行身份验证。我有一个习惯UserDetailsUserDetailsContextMapper.

我有一个用例,用户可以临时承担单个会话的额外职责。我希望能够在用户已经登录后(即会话中)为其分配一个角色,然后在会话到期时删除该角色

我有一个user_role存储用户和角色之间关系的数据库表。在这种情况下,关系是添加到数据库中然后在会话到期时删除,还是仅存在于内存中都没有关系。无论哪种方式,我都在寻找一种在用户登录后分配角色(并立即应用)的方法。这可能吗?

4

2 回答 2

6

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 创建一个包装器,该包装器将保存额外的权限并将其包装并在会话中期开始时设置为当前的 Authentication 对象,并在会话中期结束时展开。


编辑:

正如本在评论中指出的那样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);
于 2011-09-29T07:35:07.563 回答
3

我只是遇到了同样的问题,并找到了一个更短的解决方案。我使用“之前”过滤器来检查 LDAP 用户是否有本地(db)用户,如有必要,请查找或创建它,为用户添加角色,然后通过以下方式重新验证用户:

springSecurityService.reauthenticate(user.username)

然后,用户将拥有当前的权限列表。

于 2013-09-11T15:18:47.023 回答