Skip to content

[#2083] bugfix: added Shiro core additional ClassLoader to be used when seria… #2085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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();
}
}

/**
55 changes: 52 additions & 3 deletions lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ public final class ClassUtils {
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtils.class);

private static final ThreadLocal<ClassLoader> 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 <T> Class<T> 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<Method> 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
*/