首页 文章

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

提问于
浏览
0

我正在尝试使用Apache Shiro实现基本身份验证 . 我将监听器和过滤器添加到我的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>

并将authcBasic过滤器应用于我的shiro.ini文件中的受保护页面:

[urls]
/api/users = anon
/login.html = anon
/** = authcBasic

另外,我使用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对象POST到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());
    }
}

(这几乎是在tutorial from the Shiro web page之后 . )

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

我已经通过调试到以下几点来跟踪问题:Sha256CredentialsMatcher从数据库中检索与所接收的用户名匹配的salt,并使用此salt对所接收的密码进行哈希处理 . 然后,它将结果与已存储在数据库中的此用户名的密码哈希进行比较 . 在我的情况下,即使客户端发送了正确的用户名和密码组合,这些哈希也永远不会匹配 . 看起来好像盐和/或密码哈希在将它们存储在数据库中并再次检索它们的过程中发生了变化 . 或者好像以某种方式 Sha256Hash(...) 构造函数和 Sha256CredentialsMatcher.doCredentialsMatch(...) 方法在散列相同密码时得到不同的结果 . 但我不知道这可能会发生什么 .

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

public class UserService
{
    ... 
    private static void encryptPassword(User user) throws UnsupportedEncodingException
    {
        ... 
        user.setSalt(salt.toString());
...

两种变体都会发生错误 .

2 回答

  • 0

    我使用以下,它的工作原理

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

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

  • 0

    差不多三个月后,我找到了解决方案 . : - /与Shiro文档中的教程相比,您不能简单地实例化ByteSource对象并将其用作salt(至少不是版本1.2.3) . 相反,您需要从ByteSource对象获取String并将其用作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);
    }
    

相关问题