Java 基本身份验证的Apache Shiro凭据匹配始终失败

Java 基本身份验证的Apache Shiro凭据匹配始终失败,java,authentication,shiro,Java,Authentication,Shiro,我正在尝试用ApacheShiro实现基本身份验证。我已将侦听器和筛选器添加到我的web.xml文件中: <listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name>

我正在尝试用ApacheShiro实现基本身份验证。我已将侦听器和筛选器添加到我的web.xml文件中:

<listener>
 <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<filter>
 <filter-name>ShiroFilter</filter-name>
 <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>ShiroFilter</filter-name>
 <url-pattern>/*</url-pattern>
 <dispatcher>REQUEST</dispatcher>
 <dispatcher>FORWARD</dispatcher>
 <dispatcher>INCLUDE</dispatcher>
 <dispatcher>ERROR</dispatcher>
</filter-mapping>
另外,我使用Sha256CredentialsMatcher和JdbcRealm子类作为领域:

[main]
jdbcRealm = my.package.SaltColumnJDBCRealm
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
sha256Matcher.storedCredentialsHexEncoded = false
sha256Matcher.hashIterations = 1024 
jdbcRealm.credentialsMatcher = $sha256Matcher
jdbcRealm.authenticationQuery = SELECT password, salt FROM User WHERE email = ?
builtInCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $builtInCacheManager
securityManager.realms = $jdbcRealm
领域类别:

public class SaltColumnJDBCRealm extends org.apache.shiro.realm.jdbc.JdbcRealm
{
    public SaltColumnJDBCRealm()
    {
        this.setSaltStyle(SaltStyle.COLUMN);
    }
}
通过将JSON对象发布到RESTful Web服务,可以将用户添加到数据库:

import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.util.ByteSource;

@Path("/users")
@RequestScoped
public class UserService
{
    @EJB
    UserDao userDao;

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void register(User user)
    {
        try
        {
            encryptPassword(user);
            userDao.persist(user);
        }
        catch(InvalidEntityException | UnsupportedEncodingException e)
        {
            throw new WebApplicationException(e);
        }
    }

    private static void encryptPassword(User user) throws UnsupportedEncodingException
    {
        RandomNumberGenerator rng = new SecureRandomNumberGenerator();
        ByteSource salt = rng.nextBytes();

        String hashedPasswordBase64 = new Sha256Hash(user.getPassword(), salt, 1024).toBase64();
        user.setPassword(hashedPasswordBase64);
        user.setSalt(salt.toBase64());
    }
}
(这基本上遵循了以下原则。)

新用户的注册工作正常,当客户端尝试进行身份验证时,用户名和密码会毫无问题地到达服务器。但是登录总是失败

我已经通过调试跟踪到以下问题: Sha256CredentialsMatcher从数据库中检索与接收到的用户名匹配的salt,并使用该salt对接收到的密码进行哈希运算。之后,它将结果与数据库中已存储的该用户名的密码哈希进行比较。只是在我的例子中,这些散列永远不会匹配,即使客户端发送了用户名和密码的正确组合。在将salt和/或密码散列存储在数据库中并再次检索它们的过程中,它们似乎发生了更改。或者好像
Sha256Hash(…)
构造函数和
Sha256CredentialsMatcher.doCredentialsMatch(…)
方法在对同一密码进行哈希运算时得到了不同的结果。但我不知道这怎么可能发生

我已经尝试将salt存储在数据库中,作为
salt.toString()
而不是
salt.toBase64()


这两种变体都会发生错误。

我使用了下面的方法,它可以正常工作

credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=SHA-256
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
credentialsMatcher.hashSalted = true

您的实现中缺少部分
credentialsMatcher.**hashSalted=true**

将近三个月后,我找到了解决方案:-/ 与Shiro文档中的教程不同,您不能简单地实例化ByteSource对象并将其用作salt(至少在版本1.2.3中不是这样)。相反,您需要从ByteSource对象获取一个字符串,并将其用作salt。我选择获取Base64编码字符串,因此我的
encryptPassword
方法现在如下所示:

private static void encryptPassword(User user) throws UnsupportedEncodingException
{
    RandomNumberGenerator rng = new SecureRandomNumberGenerator();
    ByteSource byteSource = rng.nextBytes();
    String salt = byteSource.toBase64();
    String hashedPasswordBase64 = new Sha256Hash(user.getPassword(), salt, 1024).toBase64();
    user.setPassword(hashedPasswordBase64);
    user.setSalt(salt);
}

我尝试了这种配置,不幸的是没有成功。由于我的Shiro版本(1.2.3)不推荐使用
credentialsMatcher.hashSalted
属性,因此我尝试使用和不使用此属性,均未成功。无论如何,你的帖子告诉我,
Sha256CredentialsMatcher
也被弃用了,所以从现在起我将继续使用
HashedCredentialsMatcher
。但匹配问题依然存在。
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=SHA-256
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
credentialsMatcher.hashSalted = true
private static void encryptPassword(User user) throws UnsupportedEncodingException
{
    RandomNumberGenerator rng = new SecureRandomNumberGenerator();
    ByteSource byteSource = rng.nextBytes();
    String salt = byteSource.toBase64();
    String hashedPasswordBase64 = new Sha256Hash(user.getPassword(), salt, 1024).toBase64();
    user.setPassword(hashedPasswordBase64);
    user.setSalt(salt);
}