1

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

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

    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 中的这一行尚未执行。图像底部的 Watch 语句显示 .getAuthentication != null 返回 true

预处理执行

2) 在 SecurityContextPersistenceFilter 中清除 SecurityContextHolder 之前: 安全前上下文清除

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

4) getAuthentication() 现在在渲染视图/结果之前为空 在此处输入图像描述

为了澄清,我试图从自定义标记库中访问 springSecurityService.currentUser ,该标记库在布局中呈现我的页面标题。

因此,在 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])
}
4

2 回答 2

1

我遇到了同样的问题并设法找到了它。我假设您使用的是spring security core plugin。根本问题是插件注册了一个没有 DispatcherType.ASYNC 的应用程序过滤器。如果您查看 Spring 文档,spring security 支持 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
    }
}
于 2018-01-12T08:33:07.083 回答
0

您是在控制器还是过滤器中执行此操作(我的意思是“过滤器”而不是“拦截器”)?

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

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

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

于 2017-02-19T06:27:45.017 回答