Spring Security 自定义认证和密码编码

53

有没有教程或者有人可以指导我如何使用Spring-Security实现以下任务:

任务:

我需要从数据库中获取认证用户名的盐值,并用它来加密登录页面提供的密码,以将其与存储的加密密码进行比较(即验证用户)。

附加信息:

我使用自定义的数据库结构。通过自定义的UserDetailsService创建一个UserDetails对象,而UserDetailsService则使用自定义的DAOProvider从数据库中获取信息。

我的security.xml文件目前如下:

<authentication-manager>
    <authentication-provider user-service-ref="userDetailsService">
    </authentication-provider>
</authentication-manager>

现在我猜我需要

        <password-encoder hash="sha" />

但还有什么方法?我该如何告诉Spring Security使用数据库提供的盐来编码密码?


编辑:

我发现这篇SO文章很有启发性,但并不足够:如果我在xml中定义一个盐来源供密码编码器使用,就像这样:

        <password-encoder ref="passwordEncoder">                
            <salt-source ref="saltSource"/>
        </password-encoder>
我需要编写一个自定义的SaltSource来使用我的自定义盐。但是在UserDetails对象中找不到这个属性。所以... 备选方案1: 我可以使用自定义的UserDetails实现类,该实现类可能具有salt属性吗?
<beans:bean id="saltSource" class="path.to.MySaltSource"
    p:userPropertyToUse="salt"/>

并且

@Service("userDetailsService") 
public class UserDetailsServiceImpl implements UserDetailsService {
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException, DataAccessException {

        // ...
        return buildUserFromAccount(account);
    }

    @Transactional(readOnly = true)

    UserDetailsImpl buildUserFromAccount(Account account){

        // ... build User object that contains salt property
}

自定义用户类:

public class UserDetailsImpl extends User{

    // ...

    private String salt;

    public String getSalt() { return salt; }

    public void setSalt(String salt) { this.salt = salt; }
}

security.xml:

<authentication-manager>
    <authentication-provider user-service-ref="userDetailsService">
        <password-encoder hash="sha">                
        <salt-source ref="saltSource"/>
    </password-encoder>
    </authentication-provider>
</authentication-manager>

<beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource" p:userPropertyToUse="salt"/>


替代方案2:

否则我将不得不将我的accountDAO注入到SaltSource中,从数据库中提取给定userName的salt。

但是:Spring Security如何调用SaltSource?总是使用saltSource.getSalt(userDetails)吗?

那么我只需要确保我的SaltSource使用accountDAO上的userDetails.accountName来检索salt。


Edit2:

刚刚了解到,我的方法已经过时了:(所以我想我会使用StandardPasswordEncoder(我仍然需要弄清楚如何使用)。

顺便说一下:我使用扩展User类的自定义UserDetails类实现了第一个选项,只需添加一个salt属性即可通过userPropertyToUse将其传递给SaltSource,就像在Edit 1中提到的那样...


EDIT 3:

刚刚让StandardPasswordEncoder工作了,所以我在这里留下一些提示:

使用StandardPasswordEncoder进行身份验证:

<beans:bean id="encoder" 
    class="org.springframework.security.crypto.password.StandardPasswordEncoder">
</beans:bean>


<authentication-manager>
    <authentication-provider user-service-ref="userDetailsService">
        <password-encoder ref="encoder" />         
    </authentication-provider>
</authentication-manager>

据我所知,这需要使用版本为3.1.0.RC的spring-security-crypto模块。无法找到任何拥有3.0版本的存储库(即使某处列出了包括3.0.6在内的版本)。此外,文档中提到了spring security 3.1,因此我想我会选择它。

创建用户时(对于我来说只有管理员才能这样做),我只需使用

        StandardPasswordEncoder encoder = new StandardPasswordEncoder();
        String result = encoder.encode(password);

而我已经完成了。

Spring Security会在将密码字符串存储到数据库之前随机创建一个盐并将其添加到密码字符串中,因此不再需要盐列

但是,用户也可以提供全局盐作为构造函数参数(new StandardPasswordEncoder("12345");),但我不知道如何设置安全配置以从bean中检索该值,而不是使用静态字符串提供<constructor-arg name="secret" value "12345" />。但我不知道这有多重要。

3个回答

35

我会标记这个问题已解决,因为我解决了我的问题,没有其他评论或答案:

编辑1-备选方案1回答了原始的问题。

但是我不得不了解到,自定义密码加盐是一种遗留方法,在Spring Security 3.1中不再需要,正如我在编辑3中所描述的那样,我留下了一些关于如何使用StandardPasswordEncoder进行自动盐处理并将其与密码一起存储的指针。


1
你的帖子真的帮了我,我遇到了非常类似的问题。自己解决加一分 ^^ - user1431729
5
建议使用BCryptPasswordEncoder代替StandardPasswordEncoder。从安全性和与其他语言的互操作性两个方面来看,BCryptPasswordEncoder都是更好的选择。 - Farm

5

0
要从bean中检索全局salt,请使用Spring表达式语言或SpEL。
<beans:bean id="encoder" 
        class="org.springframework.security.crypto.password.StandardPasswordEncoder">
    <constructor-arg value="#{someotherBean.somePropertyWithAGetterMethod"/>
</beans:bean>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接