我正在尝试使用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对象发布到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对所接收的密码进行哈希处理。然后,它将结果与已存储在数据库中的此用户名的密码哈希进行比较。在我的情况下,即使客户端发送了正确的用户名和密码组合,这些哈希也永远不会匹配。看起来好像盐和/或密码哈希在将它们存储在数据库中并再次检索它们的过程中发生了变化。或者好像以某种方式{@ 1}}构造函数和Sha256Hash(...)
方法在散列相同密码时得到不同的结果。但我不知道如何发生这种情况。
我已经尝试将salt存储在数据库中Sha256CredentialsMatcher.doCredentialsMatch(...)
而不是salt.toString()
:
salt.toBase64()
两种变体都会出现错误。
答案 0 :(得分:0)
我使用以下功能
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=SHA-256
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
credentialsMatcher.hashSalted = true
您的实施中缺少部分credentialsMatcher.**hashSalted = true**
。
答案 1 :(得分:0)
After almost three months I found the solution. :-/
In contrast to the tutorial from the Shiro documentation, you cannot simply instantiate a ByteSource object and use it as the salt (at least not with version 1.2.3). Instead, you need to obtain a String from the ByteSource object and use this as the salt. I chose to obtain a Base64-encoded String, so my encryptPassword
method now looks like this:
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);
}