Java 如何为无状态基本身份验证api提供密码哈希算法?

Java 如何为无状态基本身份验证api提供密码哈希算法?,java,spring,security,bcrypt,Java,Spring,Security,Bcrypt,我有一个springwebserviceapi,它通过属性文件对用户进行身份验证。到目前为止,密码存储在bcrypt算法中 问题:我的api是无状态的,因此任何basic auth请求都将强制bcrypt身份验证重新计算,导致每个请求延迟约100ms 问题:建议使用哪种算法加密用户密码(无论是在属性文件还是数据库中),这些密码将在无状态api上的每个请求上使用 重点关注身份验证性能,但不忽略安全性。我不想加密密码,而是使用令牌。一个这样的解决方案是(Json Web令牌),Spring提供了对它

我有一个
spring
webserviceapi,它通过属性文件对用户进行身份验证。到目前为止,密码存储在
bcrypt
算法中

问题:我的api是无状态的,因此任何
basic auth
请求都将强制bcrypt身份验证重新计算,导致每个请求延迟约100ms

问题:建议使用哪种算法加密用户密码(无论是在属性文件还是数据库中),这些密码将在无状态api上的每个请求上使用


重点关注身份验证性能,但不忽略安全性。

我不想加密密码,而是使用令牌。一个这样的解决方案是(Json Web令牌),Spring提供了对它们的开箱即用支持

你可以看一个例子


您可以更进一步(我就是这么做的)将所有这些委托给OAuth提供者。我通常使用它。

最后,我决定创建一个缓存身份验证处理程序。它将密码从基本身份验证和所属的哈希bcrypt字符串中兑现。但前提是身份验证成功

通过这种方式,我可以使用有效的身份验证加快已知客户机的速度(因为我不必为它们重新计算bcrypt哈希匹配),但仍然保留了
bcrypt
算法带来的蛮力优势

以下是特定于spring的实现:

static class CachingDelegatingPasswordEncoder implements PasswordEncoder {
        private final PasswordEncoder delegate = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        private final Map<String, String> cache = new HashMap<>();

        @Override
        public String encode(CharSequence rawPassword) {
            return delegate.encode(rawPassword);
        }

        @Override
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            String cachedPassword = cache.get(rawPassword);
            if (cachedPassword != null && StringUtils.equals(cachedPassword, encodedPassword))
                return true;

            boolean match = delegate.matches(rawPassword, encodedPassword);
            if (match) cache.put(rawPassword.toString(), encodedPassword);

            return match;
        }
}

@Configuration
static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(AuthenticationManagerBuilder builder) throws Exception {
        DaoAuthenticationProvider p = new DaoAuthenticationProvider();
        p.setPasswordEncoder(new CachingDelegatingPasswordEncoder());
        p.setUserDetailsService(userDetailsService);
        p.afterPropertiesSet();
        builder.authenticationProvider(p);
    }
}
静态类CachingDelegatingPasswordEncoder实现PasswordEncoder{
私有最终PasswordEncoder委托=PasswordEncoderFactorys.createDelegatingPasswordEncoder();
私有最终映射缓存=新HashMap();
@凌驾
公共字符串编码(字符序列和密码){
返回delegate.encode(密码);
}
@凌驾
公共布尔匹配(CharSequence rawPassword、String encodedPassword){
字符串cachedPassword=cache.get(rawPassword);
if(cachedPassword!=null&&StringUtils.equals(cachedPassword,encodedPassword))
返回true;
布尔匹配=delegate.matches(rawPassword,encodedPassword);
if(匹配)cache.put(rawPassword.toString(),encodedPassword);
复赛;
}
}
@配置
静态类身份验证配置扩展了GlobalAuthenticationConfigurerAdapter{
@自动连线
私有用户详细信息服务用户详细信息服务;
@凌驾
public void configure(AuthenticationManagerBuilder)引发异常{
DaoAuthenticationProvider p=新的DaoAuthenticationProvider();
p、 setPasswordEncoder(新的CachingDelegatingPasswordEncoder());
p、 setUserDetailsService(userDetailsService);
p、 后属性set();
builder.authenticationProvider(p);
}
}

分发包含内置过期且由应用程序签名的身份验证令牌/会话id的cookie不会添加状态,但会删除恒定的哈希开销。我知道,但我无法将cookie/token添加到身份验证中。我只需要坚持使用
basic auth
stateless。如果你没有添加令牌,你会在每次通话中传递用户名和密码吗?这比添加令牌更不安全。看看JWT,它是建立在基本身份验证的基础上的。速度慢是bcrypt的主要优点之一。你要求取消这项福利。你还要求对密码进行“加密”,这是一个糟糕的想法,如果你不知道散列和加密之间的区别,那么无论如何都不应该允许你接近这样一个安全系统的实现。很抱歉这么直截了当,但这真的不是一个适合你自己的地方。好吧,我唯一的目标是有一个相当安全的属性文件和一群用户。如果这个属性文件被泄露,我必须将密码散列存储。不幸的是,除了
基本身份验证
,我无法切换到任何其他身份验证。因此,我必须在安全性和性能之间找到一个平衡点
bcrypt
正如我所写的那样,我使用的哈希算法似乎不是正确的。现在在应用程序内存中有明文密码,这也不是一个好主意。在这一点上,您的密码并没有比将密码保存在明文文件中好多少。有一个很好的理由可以让您将密码作为
CharSequence
而不是
String
——原因是您希望在使用密码后尽快清理内存<代码>字符串s可以插入字符串池中,从而使密码在内存中保留的时间更长。