Широ генерирует исключение UnavailableSecurityManagerException в настраиваемом сериализаторе JSON - PullRequest
0 голосов
/ 18 октября 2018

Я работаю над REST API веб-приложения на базе TomEE 7, которое использует Shiro 1.3.2 для безопасности.Когда поступает запрос API, создаются SecurityManager и Subject, и последний привязывается к SubjectThreadState.Я могу вызвать SecurityUtils.getSubject() в любом месте кода конечной точки, и тема всегда доступна.

Однако возникают проблемы, когда я пытаюсь сделать то же самое в моем настраиваемом сериализаторе JSON.Он только сериализует определенные поля в некоторых классах, поэтому я регистрирую его для каждого поля с использованием этой аннотации:

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

Я написал свой сериализатор на основе кода примера на этой странице в разделе "2.7. @JsonSerialize ".Сериализатору необходимо выполнить поиск в кэше, и для этого у него должен быть субъект Широ.Нет, потому что, благодаря аннотации выше, я не вызываю сериализатор вручную;вместо этого Джерси называет это.Это исключение выдается ( уточнение: при попытке запустить 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.Тем не менее, это определенно неправильный способ сделать это, потому что тогда конечная точка будет эффективно отправлять и получать строки вместо объектов, для которых они предназначены.

Я не очень разбираюсь во внутренней работеШиро или Джексон, но похоже, что сериализация выполняется внутри другого потока, где Широ SubjectThreadState не существует.Хотя, если многопоточность действительно является причиной, я не могу понять, почему Thread.currentThread().getName() возвращает одинаковое значение как внутри, так и снаружи сериализатора, как и Thread.currentThread().getId().

. Я пробовал множество вещей, но безрезультатно., включая:

  • Обновление до Shiro 1.4.0.
  • Обновление Джексона с 2.7.5 до 2.9.7.
  • Сохранение экземпляра SecurityManager, который являетсясоздается в начале вызова API внутри статической переменной ThreadLocal класса serialiser.
  • Написание собственной реализации MessageBodyWriter, которая, что неудивительно, вызывается точно так же.
  • Установка параметра staticSecurityManagerEnabled на true в конфигурации ShiroFilter в моем web.xml.

Может кто-нибудь подсказать, как я мог бы сделать SecurityManager (или 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создал и связал:

<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.

1 Ответ

0 голосов
/ 22 октября 2018

Взгляните на документ Тематической ассоциации Shiro's doc

...