Shiro使用DefaultSecurityManager抱怨“没有与id xxx的会话”

时间:2013-01-25 06:55:09

标签: security shiro

我在长期运行的应用程序中使用Apache Shiro 1.2.0,该应用程序从队列中读取消息并采取措施。所采取的操作需要一个Shiro身份验证会话,因此我实现了一个“ActAsAuthenticationToken”和自定义凭据匹配器,它允许我们只使用用户名登录。我正在使用DefaultSecurityManager,只有我的自定义领域和主题工厂注入。其他一切都应该是默认的。

在配置的时候,一切都运行良好一段时间,但是由于应用程序运行了很长时间(不是那么长 - 就像一整天),每当我做任何需要会话的事情时,我就开始得到这个堆栈跟踪: / p>

Caused by: org.apache.shiro.session.UnknownSessionException: There is no session with id [f5b7c3bf-2c53-40e9-a707-37f4265970aa]
    at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170)
    at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSessionFromDataSource(DefaultSessionManager.java:236)
    at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSession(DefaultSessionManager.java:222)
    at org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doGetSession(AbstractValidatingSessionManager.java:118)
    at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupSession(AbstractNativeSessionManager.java:105)
    at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupRequiredSession(AbstractNativeSessionManager.java:109)
    at org.apache.shiro.session.mgt.AbstractNativeSessionManager.getAttribute(AbstractNativeSessionManager.java:206)
    at org.apache.shiro.session.mgt.DelegatingSession.getAttribute(DelegatingSession.java:141)
    at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
    at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
    at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
    at com.factorlab.security.FactorlabDelegatingSubject.getUser(FactorlabDelegatingSubject.java:34)
    at com.factorlab.security.FactorlabDelegatingSubject.getUser(FactorlabDelegatingSubject.java:10)
    at com.factorlab.persistence.AbstractEntityDao.getCurrentUser(AbstractEntityDao.java:227)
    at com.factorlab.persistence.AbstractEntityDao.fireEvent(AbstractEntityDao.java:215)
    at com.factorlab.persistence.AbstractEntityDao.saveOrUpdate(AbstractEntityDao.java:190)
    at com.factorlab.persistence.AbstractEntityDao.saveOrUpdate(AbstractEntityDao.java:177)
    at com.factorlab.persistence.AbstractEntityDao.saveOrUpdate(AbstractEntityDao.java:38)
    at sun.reflect.GeneratedMethodAccessor106.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
    at $Proxy72.saveOrUpdate(Unknown Source)
    at com.factorlab.observations.sales.OpportunityScoreUpdateServiceImpl.receiveOpportunityEvent(OpportunityScoreUpdateServiceImpl.java:83)
    at sun.reflect.GeneratedMethodAccessor103.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy76.receiveOpportunityEvent(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor102.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:69)
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:84)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:57)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:102)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:102)
    at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:126)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:227)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:127)
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:73)
    ... 49 more

真正奇怪的部分(据我所知)是我成功登录(或至少表明我在收到错误之前已经过身份验证:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
@Trace(dispatcher = true)
public void receiveOpportunityEvent(EntityEvent<Opportunity> event) {
    sessionFactory.getCurrentSession().refresh(event.getEntity());
    log.info("OpportunityScoreUpdateService receiveOpportunityEvent: " + event);

    //
    //
    // Here we see that we are either authenticated or we log in successfully
    //
    //
    if (!securityUtils.getSubject().isAuthenticated()) {
        try {
            securityUtils.getFactorlabSubject().login(new ActAsAuthenticationToken(event.getEventUsername()));
        } catch (RuntimeException e) {
            log.error("Could not log in user " + event.getEventUsername() + ": " + e.getMessage(), e);
            return;
        }
    }
    if (event.getEntity() instanceof ObservedOpportunity) {
        ObservedOpportunity opportunity = (ObservedOpportunity) event.getEntity();
        opportunity = (ObservedOpportunity) opportunityDao.getById(opportunity.getId(), SkippedCheck.PERMISSION, SkippedCheck.DELETED);
        if (!opportunity.isDeleted()) {
            List<Stage> stages = stageDao.getAllByZone(opportunity.getZone(), SkippedCheck.PERMISSION);
            Map<Stage, Double> originalScoresByStage = new HashMap<Stage, Double>();
            Map<Stage, Double> newScoresByStage = new HashMap<Stage, Double>();
            final Double originalTotal = opportunity.getTotalScore();
            for (Stage stage : stages) {
                originalScoresByStage.put(stage, opportunity.getScoreByStage(stage));
                double score = calculator.getScoreForOpportunityAndStage(opportunity, stage);
                opportunity.setScoreByStage(stage, score);
                newScoresByStage.put(stage, opportunity.getScoreByStage(stage));
            }

            final double newTotalScore = calculator.getTotalScoreForOpportunity(opportunity);
            opportunity.setTheTotalScore(newTotalScore);
            final boolean scoreChanged = originalTotal == null ||
                    Math.round(originalTotal) != Math.round(newTotalScore) ||
                    checkStageScoresChanged(originalScoresByStage, newScoresByStage);
            if (scoreChanged) {
                opportunity.setScoreCalculated(new Date());

                //
                //
                // Here is where we get the exception
                //
                //
                opportunityDao.saveOrUpdate(opportunity, SkippedCheck.PERMISSION);
            } else {
                opportunityDao.refresh(opportunity);
            }
        }
    }
}

可能导致此异常的原因是什么?

3 个答案:

答案 0 :(得分:6)

我收到此错误并发现在调用subject.login(credentials)修复它之前完全破坏了任何现有会话。

// Login the user
private Subject loginUser()
{
  ensureUserIsLoggedOut();
  Subject subject = SecurityUtils.getSubject();
  subject.login(credentials);
}

支持程序是:

// Logout the user fully before continuing.
private void ensureUserIsLoggedOut()
{
    try
    {
        // Get the user if one is logged in.
        Subject currentUser = SecurityUtils.getSubject();
        if (currentUser == null)
            return;

        // Log the user out and kill their session if possible.
        currentUser.logout();
        Session session = currentUser.getSession(false);
        if (session == null)
            return;

        session.stop();
    }
    catch (Exception e)
    {
        // Ignore all errors, as we're trying to silently 
        // log the user out.
    }
}

答案 1 :(得分:3)

Shiro正在验证SecuritySubject的凭据,SecuritySubject存储在Session中。因此,在一段时间不活动后,您的会话很可能会过期。您可以在web.xml中更改到期时间,也可以使用Shiro rememberMe功能,但您的客户端必须支持cookie。在rememberMe函数之后,SecuritySubject将获得不同的会话,并将针对isAuthenticated返回false,但isRemembered将返回true。

会话永不过期当您的会话永不过期时,这会产生另一个问题。它很可能会让你内存不足,因为你的Web容器最有可能使用内存会话管理器。

<session-config>
    <session-timeout>-1</session-timeout>
</session-config>

Shiro rememberMe http://shiro.apache.org/java-authentication-guide.html

//Example using most common scenario:
//String username and password.  Acquire in
//system-specific manner (HTTP request, GUI, etc)

UsernamePasswordToken token =
 new UsernamePasswordToken( username, password );

//”Remember Me” built-in, just do this:
token.setRememberMe(true);

答案 2 :(得分:-1)

我们可以在shiro中禁用会话存储。

org.apache.shiro.mgt.DefaultSessionStorageEvaluator类包含一个名为sessionStorageEnabled的标志。我们可以把它弄错。

我在spring应用程序上下文中使用以下内容来不使用会话存储。

<bean id="defaultSessionStorageEvaluator" class="org.apache.shiro.mgt.DefaultSessionStorageEvaluator">
        <property name="sessionStorageEnabled" value="false" />

<bean id="defaultSubjectDAO" class="org.apache.shiro.mgt.DefaultSubjectDAO">
        <property name="sessionStorageEvaluator" ref="defaultSessionStorageEvaluator" />
    </bean>