Grails Spring Security:如何允许密码为空?

Posted

技术标签:

【中文标题】Grails Spring Security:如何允许密码为空?【英文标题】:Grails Spring Security: how can I allow the password null? 【发布时间】:2015-10-19 15:51:58 【问题描述】:

我正在使用 Grails Spring 安全插件。我有一个要求,对于一种用户,不需要密码。所以我覆盖了默认用户域中的密码约束:

class SecUser 
    ...    
    static constraints = 
        username blank: false, unique: true
        password blank: false, nullable: true
    
    ...

但这会导致很多问题:

    用户域中的beforeInsert 中断,因为springSecurityService.encodePassword(password) 不能接受空值;然后我覆盖beforeInsert:

    def beforeInsert() 
        if (someCondition) 
            super.beforeInsert()
            passwordChangeDate = new Date()
        
    
    

    UserDetails 类中断,因为构造函数不能接受密码作为空值,所以我覆盖了UserDetails

    import grails.plugin.springsecurity.userdetails.GrailsUser
    
    import org.springframework.security.core.GrantedAuthority
    import org.springframework.security.core.userdetails.User.AuthorityComparator
    import org.springframework.util.Assert
    
    class ILUserDetails extends GrailsUser 
    
        final String name
    
        ILUserDetails(String username, String password, boolean enabled,
            boolean accountNonExpired, boolean credentialsNonExpired,
            boolean accountNonLocked,
            Collection<GrantedAuthority> authorities,
            long id, String name) 
            //Override User 
            if (((username == null) || "".equals(username)))  // Allow null for password
                throw new IllegalArgumentException(
                    "Cannot pass null or empty values to constructor");
            
    
            this.username = username;
            this.password = password;
            this.enabled = enabled;
            this.accountNonExpired = accountNonExpired;
            this.credentialsNonExpired = credentialsNonExpired;
            this.accountNonLocked = accountNonLocked;
            this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
            this.id=id
            this.name = name
        
    
        private static SortedSet<GrantedAuthority> sortAuthorities(
            Collection<? extends GrantedAuthority> authorities) 
            Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
            // Ensure array iteration order is predictable (as per
            // UserDetails.getAuthorities() contract and SEC-717)
            SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(
                new AuthorityComparator());
    
            for (GrantedAuthority grantedAuthority : authorities) 
                Assert.notNull(grantedAuthority,
                "GrantedAuthority list cannot contain any null elements");
                sortedAuthorities.add(grantedAuthority);
            
    
            return sortedAuthorities;
        
    
    

    然后我覆盖beforeInsertUserDetails,一个奇怪的bug发生了:

    java.lang.NoSuchMethodError: grails.plugin.springsecurity.userdetails.GrailsUser: method <init>()V not found
    com.app.security.ILUserDetails.<init>(ILUserDetails.groovy)
    sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    org.springsource.loaded.ri.ReflectiveInterceptor.jlrConstructorNewInstance(ReflectiveInterceptor.java:986)
    org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
    org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
    org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
    

现在,我不确定让密码可以为空是否是个好主意。有人能给我一些建议吗?

【问题讨论】:

【参考方案1】:

之前的ILUserDetails 有错误,因为子类无法访问私有属性。所以我根据GrailsUser.javaUser.java重写了整个User类。

//Rewrite according to GrailsUser and User
//https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/core/userdetails/User.java
//https://github.com/grails-plugins/grails-spring-security-core/blob/master/src/java/grails/plugin/springsecurity/userdetails/GrailsUser.java
public class ILUser implements UserDetails, CredentialsContainer 
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
    private static final long serialVersionUID = 1;
    private final Object id;
    private final String name;

    // ~ Constructors
    // ===================================================================================================

    /**
     * Construct the <code>User</code> with the details required by
     * @link org.springframework.security.authentication.dao.DaoAuthenticationProvider.
     *
     * @param password the password that should be presented to the
     * <code>DaoAuthenticationProvider</code>
     */
    public ILUser(String username, String password, boolean enabled,
            boolean accountNonExpired, boolean credentialsNonExpired,
            boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities, Object id, String name) 

        if (((username == null) || "".equals(username)))  // Here is the key line
            throw new IllegalArgumentException(
                    "Cannot pass null or empty values to constructor");
        

        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.credentialsNonExpired = credentialsNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        this.id = id;
        this.name = name;
    

    // ~ Methods
    // ========================================================================================================

    public Collection<GrantedAuthority> getAuthorities() 
        return authorities;
    

    public String getPassword() 
        return password;
    

    public String getUsername() 
        return username;
    

    public boolean isEnabled() 
        return enabled;
    

    public boolean isAccountNonExpired() 
        return accountNonExpired;
    

    public boolean isAccountNonLocked() 
        return accountNonLocked;
    

    public boolean isCredentialsNonExpired() 
        return credentialsNonExpired;
    

    public void eraseCredentials() 
        password = null;
    

    private static SortedSet<GrantedAuthority> sortAuthorities(
            Collection<? extends GrantedAuthority> authorities) 
        Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
        // Ensure array iteration order is predictable (as per
        // UserDetails.getAuthorities() contract and SEC-717)
        SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(
                new AuthorityComparator());

        for (GrantedAuthority grantedAuthority : authorities) 
            Assert.notNull(grantedAuthority,
                    "GrantedAuthority list cannot contain any null elements");
            sortedAuthorities.add(grantedAuthority);
        

        return sortedAuthorities;
    

    private static class AuthorityComparator implements Comparator<GrantedAuthority>,
            Serializable 
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

        public int compare(GrantedAuthority g1, GrantedAuthority g2) 
            // Neither should ever be null as each entry is checked before adding it to
            // the set.
            // If the authority is null, it is a custom authority and should precede
            // others.
            if (g2.getAuthority() == null) 
                return -1;
            

            if (g1.getAuthority() == null) 
                return 1;
            

            return g1.getAuthority().compareTo(g2.getAuthority());
        
    

    /**
     * Returns @code true if the supplied object is a @code User instance with the
     * same @code username value.
     * <p>
     * In other words, the objects are equal if they have the same username, representing
     * the same principal.
     */
    @Override
    public boolean equals(Object rhs) 
        if (rhs instanceof ILUser) 
            return username.equals(((ILUser) rhs).username);
        
        return false;
    

    /**
     * Returns the hashcode of the @code username.
     */
    @Override
    public int hashCode() 
        return username.hashCode();
    

    @Override
    public String toString() 
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString()).append(": ");
        sb.append("Username: ").append(this.username).append("; ");
        sb.append("Password: [PROTECTED]; ");
        sb.append("Enabled: ").append(this.enabled).append("; ");
        sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
        sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired)
                .append("; ");
        sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");

        if (!authorities.isEmpty()) 
            sb.append("Granted Authorities: ");

            boolean first = true;
            for (GrantedAuthority auth : authorities) 
                if (!first) 
                    sb.append(",");
                
                first = false;

                sb.append(auth);
            
        
        else 
            sb.append("Not granted any authorities");
        

        return sb.toString();
    

    /**
     * Get the id.
     * @return the id
     */
    public Object getId() 
        return id;
    

【讨论】:

以上是关于Grails Spring Security:如何允许密码为空?的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Security 登录期间设置 Grails/Spring Locale - 如何?

Grails Spring-Security - 如何比较密码-

使用 Spring Security 时如何允许 CKEditor 在 Grails 中上传图像

如何使用 spring-security-core-ldap 插件在 grails 中实现 LDAP 身份验证?

如何使用 Grails Spring Security Plugin (Requestmap)

Grails、Spring Security 和 Angular JS - 如何保护 URL?