12

キューからメッセージを読み取り、アクションを実行する長時間実行アプリケーションで Apache Shiro 1.2.0 を使用しています。実行されるアクションには Shiro 認証セッションが必要なので、「ActAsAuthenticationToken」と、ユーザー名だけでログインできるカスタム認証情報マッチャーを実装しました。カスタム レルムとサブジェクト ファクトリのみが注入された DefaultSecurityManager を使用しています。それ以外はすべてデフォルトにする必要があります。

構成されているため、しばらくの間はすべて正常に機能していましたが、アプリケーションが長時間実行されたため (それほど長くはありません - 丸一日のように)、セッションを必要とする何かを行うたびに、このスタック トレースを取得し始めました。

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);
            }
        }
    }
}

この例外の原因は何ですか?

4

3 に答える 3

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.
    }
}
于 2015-06-05T17:51:00.940 に答える
3

Shiro は、Session に格納されている SecuritySubject に対して資格情報を検証しています。そのため、しばらく非アクティブな状態が続くと、セッションが期限切れになった可能性が非常に高くなります。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);
于 2013-07-26T12:49:37.773 に答える
-1

shiro でセッション ストレージを無効にできます。

org.apache.shiro.mgt.DefaultSessionStorageEvaluator クラスには、sessionStorageEnabled というフラグが含まれています。偽にすることができます。

セッションストレージを使用しないために、春のアプリケーションコンテキストで次を使用します。

<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>
于 2017-07-12T09:29:11.613 に答える