[英]Apache Shiro credentials matching for basic authentication always fails
我正在嘗試使用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());
}
}
新用戶的此注冊工作正常,並且當客戶端嘗試進行身份驗證時,用戶名和密碼可以毫無問題地到達服務器。 但是登錄總是失敗。
我已經通過調試到以下幾點來跟蹤該問題:Sha256CredentialsMatcher從數據庫中檢索與接收到的用戶名匹配的鹽,並使用此鹽對所接收的密碼進行哈希處理。 然后,它將結果與該用戶名已存儲在數據庫中的密碼哈希進行比較。 就我而言,即使客戶端發送了正確的用戶名和密碼組合,這些哈希也始終不匹配。 在將鹽和/或密碼哈希存儲到數據庫中並再次檢索它們的過程中,鹽和/或密碼哈希似乎已更改。 或好像在哈希相同的密碼時, Sha256Hash(...)
構造函數和Sha256CredentialsMatcher.doCredentialsMatch(...)
方法以不同的方式達到不同的結果。 但是我不知道這可能如何發生。
我已經嘗試過將鹽作為salt.toString()
而不是salt.toBase64()
存儲在數據庫中:
public class UserService
{
...
private static void encryptPassword(User user) throws UnsupportedEncodingException
{
...
user.setSalt(salt.toString());
...
兩個變體都會發生錯誤。
我使用以下方法並且有效
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對象並將其用作鹽(至少不能用於1.2.3版)。 相反,您需要從ByteSource對象獲取一個String並將其用作鹽。 我選擇獲取Base64編碼的String,所以我的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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.