Apache Shiro 身份验证第二次失败

Posted

技术标签:

【中文标题】Apache Shiro 身份验证第二次失败【英文标题】:Apache Shiro authentication failing second time around 【发布时间】:2015-05-17 23:47:14 【问题描述】:

我有一个使用 Apache Shiro 作为安全框架的 JavaEE Web 应用程序。我已经实现了一个自定义 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;
    

【问题讨论】:

【参考方案1】:

在您的 shiro.ini 中设置一个网络会话管理器并将其设置到您的安全管理器中。看到这有帮助。

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager

【讨论】:

我遇到了同样的问题,这对我来说是这样。另外,现在我可以从 shiro.ini 访问所有配置选项,例如 HttpOnly 标头。 (哦,好吧,白菜鸟……)【参考方案2】:

我遇到了同样的问题,我通过在浏览器上清除本地主机(或任何服务器地址)的缓存和 cookie 来简单地解决了这个问题。 以上建议都不适合我。

【讨论】:

以上是关于Apache Shiro 身份验证第二次失败的主要内容,如果未能解决你的问题,请参考以下文章

在 apache shiro 中获取令牌提交的异常身份验证失败

Apache shiro + kerberos 身份验证

Apache Shiro 身份验证自定义

Shiro又爆高危漏洞,Apache Shiro身份验证绕过漏洞安全风险通告

Apache Shiro - 使用 cn 以外的属性进行身份验证?

使用 Facebook OAuth 进行 Apache Shiro 身份验证