在 Grails 3.2.6 中,异步控制器操作无法访问 SecurityContextHolder

In Grails 3.2.6, Async controller actions lose access to SecurityContextHolder

这在 Grails 2 中不是问题,现在似乎只出现在 Grails 3 中。调用异步任务的任何控制器都无法访问 SecurityContextHolder 以在呈现视图时获取 logged-in 用户信息....

似乎在 SecurityContextPersistenceFilter 中,SecurityContextHolder.clearContext() 在 DispatcherServlet.processDispatchResult 能够呈现之前被调用,使得呈现代码无法访问存储在 SecurityContextHolder 中的 logged-in 用户信息:

    try {
        SecurityContextHolder.setContext(contextBeforeChainExecution);

        chain.doFilter(holder.getRequest(), holder.getResponse());

    }
    finally {
        SecurityContext contextAfterChainExecution = SecurityContextHolder
                .getContext();
        // Crucial removal of SecurityContextHolder contents - do this before anything
        // else.
        SecurityContextHolder.clearContext();
        repo.saveContext(contextAfterChainExecution, holder.getRequest(),
                holder.getResponse());
        request.removeAttribute(FILTER_APPLIED);

        if (debug) {
            logger.debug("SecurityContextHolder now cleared, as request processing completed");
        }
    }

起初我认为这个问题与安全上下文没有被传递到 Promise 的可运行对象(或类似的东西)有关,并且设置 springsecurity.sch.strategyName = "MODE_INHERITABLETHREADLOCAL" 无济于事。

以下是显示调试器的一些屏幕截图:

1) DispatcherServlet 中的这一行还没有执行。图片底部的观察语句显示 .getAuthentication != null returns true

2) 在 SecurityContextPersistenceFilter 中清除 SecurityContextHolder 之前:

3) 从 ha.handle 返回后,.getAuthentication() 现在为 null

4) getAuthentication() 现在在呈现之前为 null view/result

澄清一下,我正在尝试从自定义标签库中访问 springSecurityService.currentUser,该标签库在布局中呈现我页面的 header。

因此,在 layout.gsp 类型文件中:

<header id="header" class="md-whiteframe-1dp">
<g:renderHeader/></header>

renderHeader 定义如下:

def renderHeader = { attrs, body ->

    SecUser currentUser = (SecUser) accountService.activeUser

    log.info("About to render header, session.id=" + session.id +
            (currentUser?.userLogLabel ?: " user=not_logged_in"))

    out << render(template: "/header", model: [currentUser : currentUser])
}

您是在控制器还是过滤器中执行此操作(我的意思是 'filter' 而不是 'interceptor')?

因为我可以毫无问题地从自定义 TokenFilter 使用它。

这就是为什么我强烈主张将通信从业务逻辑转移到更高的过滤器和处理程序拦截器,并停止将其与注释和其他东西联系起来。他们一遍又一遍地 运行 关注这些问题。

实际上,我刚刚为 API 发布了一个更快的 Grails 版本,它解决了昨天大学

的大部分通信问题

我遇到了同样的问题并设法找到了它。我假设您正在使用 spring security core plugin. The root issue is that the plugin registers an application filter without DispatcherType.ASYNC. If you look at the Spring documentation, spring security supports async。为了修复它,我创建了这个 BeanPostProcessor 并将它放在我的应用程序上下文中。

class SpringSecurityAsyncConfigurer implements BeanPostProcessor {
    @Override
    Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
        if (bean in FilterRegistrationBean && "springSecurityFilterChainRegistrationBean".equals(beanName)) {
            //for some unknown reason the plugin only has this run for request and error, adding async as the spring documentation
            //says this is supported
            bean.setDispatcherTypes(EnumSet.<DispatcherType>of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC))
        }
        bean
    }

    @Override
    Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
        return bean
    }
}