java - Shiro 在自定义 JSON 序列化程序中抛出 UnavailableSecurityManagerException
问题描述
我正在开发基于 TomEE 7 的网络应用程序的 REST API,该应用程序使用 Shiro 1.3.2 来保证安全性。当一个 API 请求进来时,会创建 aSecurityManager
和 a Subject
,而后者绑定到 a SubjectThreadState
。我可以SecurityUtils.getSubject()
在端点代码中的任何地方调用,并且主题始终可用。
但是,当我尝试在自定义 JSON 序列化器中执行相同操作时,就会出现问题。它仅序列化某些类中的特定字段,因此我使用此注释在每个字段的基础上注册它:
@JsonSerialize(using = MySerialiser.class)
Long myRelatedItemId;
我根据此页面上“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)
我已经确认,如果我ObjectMapper().writeValueAsString()
从 API 端点代码手动调用类似的东西,一切正常。但是,这绝对不是正确的方法,因为端点将有效地发送和接收字符串,而不是它们要处理的对象。
我不太了解 Shiro 或 Jackson 的内部工作原理,但似乎序列化是在另一个线程中执行的,而 ShiroSubjectThreadState
不存在。虽然如果线程确实是原因,那么我看不出为什么Thread.currentThread().getName()
在序列化器内部和外部返回相同的值,就像Thread.currentThread().getId()
.
我尝试了很多东西都无济于事,包括:
- 升级到 Shiro 1.4.0。
- 将 Jackson 从 2.7.5 升级到 2.9.7。
SecurityManager
将在 API 调用开始时创建的实例保存在ThreadLocal
序列化程序类的静态变量中。- 编写我自己的实现
MessageBodyWriter
,毫不奇怪,它的调用方式完全相同。 - 在我的配置中将
staticSecurityManagerEnabled
参数设置为.true
ShiroFilter
web.xml
任何人都可以建议我如何使SecurityManager
(或Subject
)对序列化程序可见,当它在我的代码未启动的线程中运行时(澄清:或者,据我所知,并行运行并由 Jersey 启动)?提前致谢。
更新:
此堆栈跟踪是在序列化器内部获取的:
<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
创建和绑定的:
<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()
在最后一行之后,两条跟踪中还有 46 个相同的调用,因此我将它们排除在外。它们包含一堆org.apache.catalina.core
和org.glassfish.jersey
。
解决方案
看看 Shiro 的主题线程协会文档
推荐阅读
- c++ - 使用 X3 的可变因子改进重复指令
- verilog - 为什么在 Verilog 代码中重新分配时出现语法错误?
- ios - 检查扬声器是否在 iOS 中以编程方式工作
- azure - 使用 Start-AzStorageFileCopy 时如何修复“指定的资源不存在。HTTP 状态代码:404”错误
- mysql - 基于 CASE 的左连接
- r - 检测范围内的双日期并调整日期
- python - Flask 表单的问题仅返回为 FALSE
- android - 世博会 31.0.2:ExpoKit StripeModule 为空
- azure - 在 azure devops 上创建管道以在 azure app 服务上部署 docker 容器?
- php - 如何使用 php 插入 MySQL 并回显