Shiro在自定义JSON序列化程序中引发UnavailableSecurityManagerException

时间:2018-10-18 13:10:36

标签: java java-ee jackson jersey-2.0 shiro

我正在研究基于TomEE 7的Web应用程序的REST API,该应用程序使用Shiro 1.3.2进行安全保护。当API请求进入时,将创建SecurityManagerSubject,并将后者绑定到SubjectThreadState。我可以在端点代码中的任何地方调用SecurityUtils.getSubject(),并且该主题始终可用。

但是,当我尝试在自定义JSON序列化程序中执行相同操作时,就会出现问题。它只序列化某些类中的特定字段,因此我使用此注释在每个字段的基础上进行注册:

@JsonSerialize(using = MySerialiser.class)
Long myRelatedItemId;

我根据the example code on this page under "2.7. @JsonSerialize"编写了序列化程序。串行器需要执行高速缓存查找,为此它必须具有Shiro主题。没有,因为有了上面的注释,我没有手动调用序列化程序。相反,泽西岛称之为。引发此异常(说明:,当我尝试从序列化程序代码运行SecurityUtils.getSubject()时):

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:627)
    at org.apache.shiro.SecurityUtils.getSubject(SecurityUtils.java:56)

我已经确认,如果我从API端点代码中手动调用ObjectMapper().writeValueAsString()之类的东西,一切都会正常。但是,这绝对不是正确的方法,因为这样端点将有效地发送和接收字符串,而不是它们打算处理的对象。

我对Shiro或Jackson的内部工作了解不多,但是似乎正在另一个线程中执行序列化,而Shiro的SubjectThreadState不存在。虽然如果确实是线程原因,那么我看不出Thread.currentThread().getName()为什么在串行器内部和外部都返回相同的值,就像Thread.currentThread().getId()一样。

我尝试了很多无济于事的事情,包括:

  • 升级到Shiro 1.4.0。
  • 将Jackson从2.7.5升级到2.9.7。
  • 将在API调用开始时创建的SecurityManager实例保存在序列化程序类的静态ThreadLocal变量中。
  • 编写我自己的MessageBodyWriter实现,毫不奇怪,它的调用方式完全相同。
  • 在我的staticSecurityManagerEnabled的{​​{1}}配置中,将true参数设置为ShiroFilter

有人可以建议我如何使web.xml(或SecurityManager)在序列化程序不在我的代码启动的线程中运行时对序列化程序可见(说明:或者,据我所知,并行运行并由Jersey启动)?预先感谢。

更新:

此堆栈跟踪是在序列化器内部进行的:

Subject

这个是在我们创建并绑定<mypackage>.MySerializer.serialize() com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField() com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields() com.fasterxml.jackson.databind.ser.BeanSerializer.serialize() com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue() com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize() com.fasterxml.jackson.databind.ObjectWriter.writeValue() com.fasterxml.jackson.jaxrs.base.ProviderBase.writeTo() org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo() org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo() org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo() org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo() org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo() org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse() org.glassfish.jersey.server.ServerRuntime$Responder.processResponse() org.glassfish.jersey.server.ServerRuntime$Responder.process() org.glassfish.jersey.server.ServerRuntime$2.run() 的拦截器类中使用的:

Subject

在最后一行之后的两条跟踪中还有46个相同的调用,因此我将它们排除在外。它们包含一堆<mypackage>.MySecurityInterceptor.createSession() sun.reflect.NativeMethodAccessorImpl.invoke0() sun.reflect.NativeMethodAccessorImpl.invoke() sun.reflect.DelegatingMethodAccessorImpl.invoke() java.lang.reflect.Method.invoke() org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke() org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed() org.apache.openejb.monitoring.StatsInterceptor.record() org.apache.openejb.monitoring.StatsInterceptor.invoke() sun.reflect.GeneratedMethodAccessor111.invoke() sun.reflect.DelegatingMethodAccessorImpl.invoke() java.lang.reflect.Method.invoke() org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke() org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed() org.apache.openejb.core.interceptor.InterceptorStack.invoke() org.apache.openejb.core.stateless.StatelessContainer._invoke() org.apache.openejb.core.stateless.StatelessContainer.invoke() org.apache.openejb.core.ivm.EjbObjectProxyHandler.synchronizedBusinessMethod() org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod() org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke() org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke() com.sun.proxy.$Proxy279.getEntity() org.openapitools.api.impl.MyApiServiceImpl.getEntity() org.openapitools.api.MyApi.getEntity() sun.reflect.NativeMethodAccessorImpl.invoke0() sun.reflect.NativeMethodAccessorImpl.invoke() sun.reflect.DelegatingMethodAccessorImpl.invoke() java.lang.reflect.Method.invoke() org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke() org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run() org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke() org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch() org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch() org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke() org.glassfish.jersey.server.model.ResourceMethodInvoker.apply() org.glassfish.jersey.server.model.ResourceMethodInvoker.apply() org.glassfish.jersey.server.ServerRuntime$2.run() org.apache.catalina.core

1 个答案:

答案 0 :(得分:0)