我有一个JavaEE Web应用程序,它使用Apache Shiro作为安全框架。我已经实现了自定义JPA授权领域,因此我可以使用JPA存储我的用户帐户。我的自定义领域看起来像这样......
public class JPAAuthorizingRealm extends AuthorizingRealm {
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(JPAAuthorizingRealm.class);
public static final String REALM_NAME = "jpaRealm";
private UserAccountRepository accountRepository = null;
@SuppressWarnings("unchecked")
public JPAAuthorizingRealm() {
super();
setName("JpaRealm");
setAuthenticationCachingEnabled(false);
setCachingEnabled(false);
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256");
matcher.setHashIterations(SecurityConstants.PASSWORD_HASH_ITERATIONS);
setCredentialsMatcher(matcher);
try {
BeanManager beanManager = (BeanManager) new InitialContext()
.lookup("java:comp/BeanManager");
Bean<UserAccountRepository> accountRepositoryBean = (Bean<UserAccountRepository>) beanManager
.resolve(beanManager.getBeans(UserAccountRepository.class));
CreationalContext<?> creationalContext = beanManager
.createCreationalContext(null);
UserAccountRepository accountRepository = accountRepositoryBean
.create((CreationalContext<UserAccountRepository>) creationalContext);
this.accountRepository = accountRepository;
} catch (NamingException e) {
logger.error(
"JPAAuthorizingRealm() - exception while creating user account repository", e); //$NON-NLS-1$
}
}
<snip>
我能够成功创建用户帐户(在单独的类中),并使用可用于进行身份验证的哈希密码。我的问题是,我只能验证 一次 。如果我登录用户,我会看到我的Shiro Subject
说我已通过身份验证。但是,如果我注销,并尝试使用完全相同的凭据(甚至是完全不同的帐户)重新登录,则似乎永远不会应用登录。我认为我的Subject
在验证后会立即isAuthenticated() == true
,但在后续请求中会显示isAuthenticated() == false
。即使我打开一个完全独立的浏览器,我也会看到相同的行为。一旦我的第一个用户成功登录,我就无法登录第二个用户。
我的登录代码如下所示,并在无状态EJB中实现......
AuthenticationToken token = new UsernamePasswordToken(loginName,
password);
try {
this.subject.login(token);
} catch (IncorrectCredentialsException e) {
logger.error("authenticateUser(String, String)", e); //$NON-NLS-1$
throw new InvalidCredentialsException(e);
} catch (AuthenticationException e) {
logger.error("authenticateUser(String, String)", e); //$NON-NLS-1$
throw new UserAuthenticationException(e);
}
我的退出代码看起来像这样......
SecurityUtils.getSubject().logout();
Faces.invalidateSession();
我的shiro.ini文件如下所示:
[main]
# listener = org.apache.shiro.config.event.LoggingBeanListener
authc = org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter
authc.loginUrl = /login.xhtml
jpaRealm=com.myapp.JPAAuthorizingRealm
[urls]
/login.xhtml = authc
再一次,当我第一次验证 时,一切都按预期工作。我的主题说我在每次请求时都经过身份验证。如果我拉出一个单独的浏览器并尝试作为第二个用户进行身份验证,则身份验证永远不会通过。可能导致这种情况的任何想法?我对Apache Shiro有些新意,所以我完全有可能做错了。
更新:我已经做了一些测试,并用标准的Shiro JDBC领域取代了我的自定义JPA领域,以缩小问题所在。新的JDBC领域配置产生与以前完全相同的行为。但是,我现在已经注意到,如果我从浏览器A(Safari,登录成功)成功登录 user1 ,然后尝试在单独的浏览器上以 user2 身份登录( Firefox,登录貌似不成功)然后 BACK 到浏览器A并点击刷新,我看到自己被认证为 user1 。似乎有某种状态管理问题正在发生,但我不能为我的生活弄清楚是什么。
这是我的新shiro.ini,以及我用来制作Subject和SecurityManager对象的CDI制作人。任何人都可以帮我解决这个问题吗?这非常令人沮丧。
shiro.ini:
[main]
hashService=org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations=10000
hashService.hashAlgorithmName=SHA-256
passwordService=org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService
passwordMatcher=org.apache.shiro.authc.credential.PasswordMatcher
passwordMatcher.passwordService=$passwordService
authc = org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter
authc.loginUrl = /login.xhtml
jdbcDataSource=org.apache.shiro.jndi.JndiObjectFactory
jdbcDataSource.resourceName=java:jboss/datasources/MyDataSource
jdbcDataSource.resourceRef=true
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.authenticationQuery = SELECT hashedPassword FROM tbl_user_account WHERE loginname = ?
jdbcRealm.permissionsLookupEnabled=false
#jdbcRealm.credentialsMatcher = $sha256Matcher
jdbcRealm.credentialsMatcher=$passwordMatcher
jdbcRealm.dataSource = $jdbcDataSource
securityManager.realms = $jdbcRealm
[urls]
/login.xhtml = authc
CDI制片人:
@Singleton
public class SecurityProducer {
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(SecurityProducer.class);
private org.apache.shiro.mgt.SecurityManager securityManager;
/**
* Initializes the {@link org.apache.shiro.mgt.SecurityManager} after bean
* creation
*/
@PostConstruct
public void init() {
if (logger.isDebugEnabled()) {
logger.debug("init() - start"); //$NON-NLS-1$
}
final String iniFile = "classpath:shiro.ini";
securityManager = new IniSecurityManagerFactory(iniFile).getInstance();
SecurityUtils.setSecurityManager(securityManager);
if (logger.isDebugEnabled()) {
logger.debug("init() - end"); //$NON-NLS-1$
}
}
/**
* Produces an Apache Shiro {@link org.apache.shiro.mgt.SecurityManager}
*
* @return The security manager
*/
@Produces
@Named("securityManager")
public org.apache.shiro.mgt.SecurityManager getSecurityManager() {
if (logger.isDebugEnabled()) {
logger.debug("getSecurityManager() - start"); //$NON-NLS-1$
}
if (logger.isDebugEnabled()) {
logger.debug("getSecurityManager() - end"); //$NON-NLS-1$
}
return securityManager;
}
/**
* Produces an Apache Shiro {@link Subject}
*
* @return The subject
*/
@Produces
@Named("subject")
public Subject getSubject() {
if (logger.isDebugEnabled()) {
logger.debug("getSubject() - start"); //$NON-NLS-1$
}
Subject subject = SecurityUtils.getSubject();
if (logger.isDebugEnabled()) {
logger.debug("getSubject() - Subject subject=" + subject); //$NON-NLS-1$
}
if (subject.isAuthenticated() || subject.isRemembered()) {
boolean authenticated = subject.isAuthenticated();
boolean remembered = subject.isRemembered();
Object principal = subject.getPrincipal();
if (logger.isDebugEnabled()) {
logger.debug("getSubject() - authenticated=" + authenticated + ", remembered=" + remembered + ", principal=" + principal); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("getSubject() - User is not authenticated or remembered"); //$NON-NLS-1$
}
}
if (logger.isDebugEnabled()) {
logger.debug("getSubject() - end"); //$NON-NLS-1$
}
return subject;
}
}
答案 0 :(得分:1)
在你的shiro.ini中设置一个网络会话管理器并将其设置到你的安全管理器中。看到这有帮助。
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager
答案 1 :(得分:0)
我遇到了同样的问题,我通过在浏览器中清除本地主机(或任何服务器地址)的缓存和cookie来解决这个问题。 上述建议均不适合我。