diff --git a/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java b/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java index c67ee8bbc1..9fce0787da 100644 --- a/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java @@ -29,6 +29,7 @@ import org.apache.shiro.lang.io.Serializer; import org.apache.shiro.lang.util.ByteSource; import org.apache.shiro.lang.util.ByteUtils; +import org.apache.shiro.lang.util.ClassUtils; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.SubjectContext; @@ -509,7 +510,12 @@ protected byte[] decrypt(byte[] encrypted) { * @return the serialized principal collection in the form of a byte array */ protected byte[] serialize(PrincipalCollection principals) { - return getSerializer().serialize(principals); + ClassUtils.setAdditionalClassLoader(AbstractRememberMeManager.class.getClassLoader()); + try { + return getSerializer().serialize(principals); + } finally { + ClassUtils.removeAdditionalClassLoader(); + } } /** @@ -520,7 +526,12 @@ protected byte[] serialize(PrincipalCollection principals) { * @return the deserialized (reconstituted) {@code PrincipalCollection} */ protected PrincipalCollection deserialize(byte[] serializedIdentity) { - return getSerializer().deserialize(serializedIdentity); + ClassUtils.setAdditionalClassLoader(AbstractRememberMeManager.class.getClassLoader()); + try { + return getSerializer().deserialize(serializedIdentity); + } finally { + ClassUtils.removeAdditionalClassLoader(); + } } /** diff --git a/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java b/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java index dafbf7e8cb..e7bba5f8d9 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java @@ -43,6 +43,7 @@ public final class ClassUtils { */ private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtils.class); + private static final ThreadLocal ADDITIONAL_CLASS_LOADER = new ThreadLocal<>(); /** * SHIRO-767: add a map to mapping primitive data type @@ -75,13 +76,24 @@ protected ClassLoader doGetClassLoader() throws Throwable { /** * @since 1.0 */ - private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() { + private static final ClassLoaderAccessor CLASS_LANG_CL_ACCESSOR = new ExceptionIgnoringAccessor() { @Override protected ClassLoader doGetClassLoader() throws Throwable { return ClassUtils.class.getClassLoader(); } }; + /** + * @since 2.0.4 + */ + private static final ClassLoaderAccessor ADDITIONAL_CL_ACCESSOR = new ExceptionIgnoringAccessor() { + @Override + protected ClassLoader doGetClassLoader() throws Throwable { + ClassLoader cl = ADDITIONAL_CLASS_LOADER.get(); + return cl != null ? cl : ClassUtils.class.getClassLoader(); + } + }; + /** * @since 1.0 */ @@ -117,7 +129,15 @@ public static InputStream getResourceAsStream(String name) { LOGGER.trace("Resource [" + name + "] was not found via the thread context ClassLoader. Trying the " + "current ClassLoader..."); } - is = CLASS_CL_ACCESSOR.getResourceStream(name); + is = CLASS_LANG_CL_ACCESSOR.getResourceStream(name); + } + + if (is == null) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Resource [" + name + "] was not found via the org.apache.shiro.lang ClassLoader. Trying the " + + "additionally set ClassLoader..."); + } + is = ADDITIONAL_CL_ACCESSOR.getResourceStream(name); } if (is == null) { @@ -157,7 +177,15 @@ public static Class forName(String fqcn) throws UnknownClassException { LOGGER.trace("Unable to load class named [" + fqcn + "] from the thread context ClassLoader. Trying the current ClassLoader..."); } - clazz = CLASS_CL_ACCESSOR.loadClass(fqcn); + clazz = CLASS_LANG_CL_ACCESSOR.loadClass(fqcn); + } + + if (clazz == null) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Unable to load class named [" + fqcn + + "] from the org.apache.shiro.lang ClassLoader. Trying the additionally set ClassLoader..."); + } + clazz = ADDITIONAL_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { @@ -259,6 +287,27 @@ public static List getAnnotatedMethods(final Class type, final Class< return methods; } + /** + * Sets additional ClassLoader for {@link #getResourceAsStream(String)} and {@link #forName(String)} to use + * It is used in addition to the thread context class loader and the system class loader. + + * @param classLoader class loader to use + * @since 2.0.4 + */ + public static void setAdditionalClassLoader(ClassLoader classLoader) { + ADDITIONAL_CLASS_LOADER.set(classLoader); + } + + /** + * Removes the additional ClassLoader set by {@link #setAdditionalClassLoader(ClassLoader)}. + * This must be called to avoid memory leaks. + * + * @since 2.0.4 + */ + public static void removeAdditionalClassLoader() { + ADDITIONAL_CLASS_LOADER.remove(); + } + /** * @since 1.0 */