Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] SseEmitter.complete() throws Error:No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration. #1950

Open
1 task done
phdbutbachelor opened this issue Jan 10, 2025 · 9 comments
Labels

Comments

@phdbutbachelor
Copy link

phdbutbachelor commented Jan 10, 2025

Search before asking

  • I had searched in the issues and found no similar issues.

Question

I'm developing a Springboot Application, I want to return a SseEmitter to front end:

@PostMapping("/chat")
public SseEmitter chat(@RequestBody JsonObject data) throws IOException {
    String _conversationId = Optional.ofNullable(data.get("conversationId"))
            .map(i -> i.isJsonNull() ? null : i.getAsString()).orElse(null);
    String _cardId = data.get("cardId").getAsString();
    String _message = data.get("message").getAsString();

    Result<Card> _record = cardRepository.get(_cardId, false);
    if (_record.isOK()) {
        return llmService.chat(_conversationId, _record.data.getEnterprise().getDataBaseId(), _message);
    } else {
        SseEmitter _sse = new SseEmitter(0L);
        _sse.send(_record);
        _sse.complete();
        return _sse;
    }
}

when the program reached _sse.complete(), it throws a Exception, here are the stacks:

2025-01-10 11:47:37.780 [ERROR] @http-nio-8080-exec-8: Servlet.service() for servlet [dispatcherServlet] threw exception
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.  This is an invalid application configuration.
	at org.apache.shiro.SecurityUtils.getSecurityManager(SecurityUtils.java:123)
	at org.apache.shiro.subject.Subject$Builder.<init>(Subject.java:626)
	at org.apache.shiro.SecurityUtils.getSubject(SecurityUtils.java:56)
	at org.apache.shiro.web.servlet.ShiroHttpServletRequest.getSubject(ShiroHttpServletRequest.java:89)
	at org.apache.shiro.web.servlet.ShiroHttpServletRequest.getSession(ShiroHttpServletRequest.java:154)
	at org.springframework.web.util.WebUtils.getSessionId(WebUtils.java:359)
	at org.springframework.web.servlet.FrameworkServlet.publishRequestHandledEvent(FrameworkServlet.java:1145)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1023)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:665)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:199)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
	at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:641)
	at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:569)
	at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:538)
	at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:600)
	at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:343)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:241)
	at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:242)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:57)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:936)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
	at java.lang.Thread.run(Thread.java:750)
2025-01-10 11:47:37.780 [ERROR] @http-nio-8080-exec-8: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [java.lang.RuntimeException: Error during asynchronous dispatch] with root cause
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.  This is an invalid application configuration.
	at org.apache.shiro.SecurityUtils.getSecurityManager(SecurityUtils.java:123)
	at org.apache.shiro.subject.Subject$Builder.<init>(Subject.java:626)
	at org.apache.shiro.SecurityUtils.getSubject(SecurityUtils.java:56)
	at org.apache.shiro.web.servlet.ShiroHttpServletRequest.getSubject(ShiroHttpServletRequest.java:89)
	at org.apache.shiro.web.servlet.ShiroHttpServletRequest.getSession(ShiroHttpServletRequest.java:154)
	at org.springframework.web.util.WebUtils.getSessionId(WebUtils.java:359)
	at org.springframework.web.servlet.FrameworkServlet.publishRequestHandledEvent(FrameworkServlet.java:1145)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1023)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:665)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:199)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144)
	at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:641)
	at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:569)
	at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:538)
	at org.apache.catalina.core.AsyncContextImpl$AsyncRunnable.run(AsyncContextImpl.java:600)
	at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:343)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:241)
	at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:242)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:57)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:936)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
	at java.lang.Thread.run(Thread.java:750)
2025-01-10 11:47:37.781 [ERROR] @http-nio-8080-exec-8: Cannot render error page for request [null] as the response has already been committed. As a result, the response may have the wrong status code.
@fpapon
Copy link
Member

fpapon commented Jan 10, 2025

Can you share more info about your configuration? (Shiro version, JRE version, ini file...)

@lprimak
Copy link
Contributor

lprimak commented Jan 10, 2025

Also, is this issue SSE-specific, or are is every request failing like this?
Is this a new application that already works, or are you trying to add SSE functionality to existing working application with Shiro?

My guess here is that SSE is bypassing normal Shiro filter flow and SecurityManager is not getting set.

@phdbutbachelor
Copy link
Author

Also, is this issue SSE-specific, or are is every request failing like this? Is this a new application that already works, or are you trying to add SSE functionality to existing working application with Shiro?

My guess here is that SSE is bypassing normal Shiro filter flow and SecurityManager is not getting set.

I'm adding the SSE functionality to existing working application, and the issue is SSE-specific, the rest work well. @lprimak

@phdbutbachelor
Copy link
Author

Here are the beans:

@Bean
public ModularRealmAuthenticator authenticator(
        AuthenticationListener authenticationListener,
        ConfigUserAuthorizingRealm configUserAuthorizingRealm,
        UsernamePasswordAuthorizingRealm usernamePasswordAuthorizingRealm,
        SMSAuthorizingRealm smsAuthorizingRealm,
        WxMaPhoneNumberAuthorizingRealm wxMaPhoneNumberAuthorizingRealm
) {
    ModularRealmAuthenticator _authenticator = new DefaultModularRealmAuthenticator(configUserAuthorizingRealm, usernamePasswordAuthorizingRealm, smsAuthorizingRealm, wxMaPhoneNumberAuthorizingRealm);

    _authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
    _authenticator.setAuthenticationListeners(Collections.singletonList(authenticationListener));
    return _authenticator;
}

@Bean
public SessionManager sessionManager(
        SessionDAO sessionDAO,
        ShiroConfigurationProperties shiroConfigurationProperties,
        SessionListenerAdapter sessionListenerAdapter
) {
    DefaultWebSessionManager _sessionManager = new DefaultWebSessionManager();

    // 设置cookie
    _sessionManager.setSessionIdCookie(shiroConfigurationProperties.getSessionIdCookie());
    _sessionManager.setSessionIdUrlRewritingEnabled(false);

    _sessionManager.setSessionDAO(sessionDAO);

    // 设置session过期时间
    _sessionManager.setGlobalSessionTimeout(shiroConfigurationProperties.getSession().getTimeout() * 1000);
    // 设置session验证间隔
    _sessionManager.setSessionValidationInterval(shiroConfigurationProperties.getSession().getValidationInterval() * 1000);
    // 设置session监听器
    _sessionManager.setSessionListeners(Collections.singletonList(sessionListenerAdapter));

    return _sessionManager;
}

@Bean
public DefaultWebSecurityManager securityManager(
        ConfigUserAuthorizingRealm configUserAuthorizingRealm,
        UsernamePasswordAuthorizingRealm usernamePasswordAuthorizingRealm,
        SMSAuthorizingRealm smsAuthorizingRealm,
        WxMaPhoneNumberAuthorizingRealm wxMaPhoneNumberAuthorizingRealm,
        ModularRealmAuthenticator authenticator,
        ModularRealmAuthorizer authorizer,
        SessionManager sessionManager,
        CacheManager cacheManager,
        RememberMeManager rememberMeManager
) {
    DefaultWebSecurityManager _manager = new DefaultWebSecurityManager();

    authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
    _manager.setAuthenticator(authenticator);

    _manager.setAuthorizer(authorizer);

    // 设置认证域
    _manager.setRealms(Arrays.asList(configUserAuthorizingRealm, usernamePasswordAuthorizingRealm, smsAuthorizingRealm, wxMaPhoneNumberAuthorizingRealm));

    // 设置会话管理器
    _manager.setSessionManager(sessionManager);

    // 设置缓存管理器
    _manager.setCacheManager(cacheManager);

    // 设置记住管理器
    _manager.setRememberMeManager(rememberMeManager);

    return _manager;
}

@Bean
@ConditionalOnMissingBean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Value("${sec.username}") String username, @Value("${sec.login.url}") String loginUrl, org.apache.shiro.mgt.SecurityManager securityManager, ShiroConfigurationProperties shiroConfigurationProperties, LoginUtil loginUtil) {
    ShiroFilterFactoryBean _filterFactory = new ShiroFilterFactoryBean();
    _filterFactory.setSecurityManager(securityManager);
    Map<String, Filter> _filters = new LinkedHashMap();
    _filters.put("user", new DefaultAuthenticationFilter(username, loginUrl, loginUtil));
    _filterFactory.setFilters(_filters);
    _filterFactory.setFilterChainDefinitionMap(shiroConfigurationProperties.getFilterChainDefinitionMap());
    return _filterFactory;
}

@Bean
@ConditionalOnMissingBean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
}

@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator _creator = new DefaultAdvisorAutoProxyCreator();
    _creator.setProxyTargetClass(true);
    return _creator;
}

@Bean
@ConditionalOnMissingBean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor _advisor = new AuthorizationAttributeSourceAdvisor();
    _advisor.setSecurityManager(securityManager);
    return _advisor;
}

And the filter chain definition map is:

  filter-chain-definition:
    - /sec/login/cancel, user
    - /sec/login/**, anon
    - /error, anon
    - /app/init, anon
    - /app/layout/header, anon
    - /media/read, anon
    - /media/read/force-partial-content, anon
    - /util/cmpassport/app-id, anon
    - /util/validation/jquery, anon
    - /llm/chat, anon

the url '/llm/chat' is set to 'anon'. @fpapon

@phdbutbachelor
Copy link
Author

And the shiro version is 1.13.0, runs on Java 1.8.0_431. @fpapon

@lprimak
Copy link
Contributor

lprimak commented Jan 12, 2025

Shiro ThreadContext needs to be bound to security manager in the SSE thread. This is not currently done, since SSE bypasses typical Shiro filter flow.
I am not a Spring user, and can't guide you through this, but this is what's required to get this to work.

@phdbutbachelor
Copy link
Author

Shiro ThreadContext needs to be bound to security manager in the SSE thread. This is not currently done, since SSE bypasses typical Shiro filter flow. I am not a Spring user, and can't guide you through this, but this is what's required to get this to work.

Is there any way to disable Shiro filter in particular request url? @lprimak

@lprimak
Copy link
Contributor

lprimak commented Jan 12, 2025

There may be a better way, but I would start with overriding / implementing my own subclass of ShiroFilter.
Again, I am not a Spring user and cannot guide you on how to do that.

@fpapon
Copy link
Member

fpapon commented Jan 13, 2025

I'm not a Spring expert so cannot help about Spring integration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants