Grails + spring-security-core:如何在用户登录后分配角色?

Ben*_*Ben 4 grails spring spring-security role

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

我有一个用例,用户可以暂时承担单个会话的额外职责.我希望能够将角色分配给用户后,当会话过期时,他们已经在(即中期会议)已经登录,然后删除该角色.

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

Roa*_*ner 6

UserDetails.getAuthorities()拥有当前登录的用户的所有角色.因为你已经有了自定义UserDetails 您只需要将其他角色添加到getAuthorities()会话中途启动时返回的值,然后在会话结束时将其删除.(见下面的编辑)

即:

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();
   }
}
Run Code Online (Sandbox Code Playgroud)

或者您可以围绕原始UserDetails创建一个包装器,该包装器将保存额外的权限并将其包装并在会话开始时设置为当前的Authentication对象,并在会话结束时解包.


编辑:

正如Ben在评论中指出的那样,UserDetails.getAuthorities()只有一次被调用,在登录时,当Authentication对象被创建时 - 我忘了这一点.但是这给我们带来了正确的答案,而这次我确信它会起作用,因为我是这样做的.该Authentication.getAuthorities()是无暇顾及,而不是方法UserDetails.getAuthorities().我真的推荐一个包装器,而不是Authenticationinterace 的自定义实现,因为它将更灵活,并允许透明地支持下面的不同身份验证机制.

即:

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
}
Run Code Online (Sandbox Code Playgroud)

那么你需要做的就是在会话开始时:

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

在会议结束时:

MidSessionAuthenticationWrapper midSessionAuthentication =
    (MidSessionAuthenticationWrapper) SecurityContextHolder.getContext().getAuthentication();
Authentication authentication = midSessionAuthentication.getWrapped();
SecurityContextHolder.getContext().setAuthentication(authentication);
Run Code Online (Sandbox Code Playgroud)