版本
thymeleaf 3.0.15
现象
在thymeleaf模板中通过${session.SPRING_SECURITY_LAST_EXCEPTION.getMessage()}获取异常消息时报错
Caused by: org.springframework.expression.EvaluationException: Calling methods is forbidden for type ‘java.lang.RuntimeException’ in Thymeleaf expressions. Blacklisted classes are: [java.util.concurrent.RunnableFuture, java.util.concurrent.Executor, java.lang.Runtime, java.util.concurrent.FutureTask, java.util.concurrent.ListenableFuture, java.lang.Runnable, java.util.concurrent.Future, java.lang.Thread, java.lang.reflect.Executable, java.lang.Class, java.lang.ClassLoader, java.sql.DriverManager].
原因
在thymeleaf 3.0.15中将java.lang.Runtime前缀的类加入了黑名单不允许在模板中访问
解决
异常以Exception抛出不要以RuntimeException抛出,其他类似问题通解
源码
- 包thymeleaf-spring5-3.0.15
org.thymeleaf.spring5.expression.ThymeleafEvaluationContext
代码语言:javascript复制static final class ThymeleafEvaluationContextACLMethodResolver extends ReflectiveMethodResolver {
ThymeleafEvaluationContextACLMethodResolver() {
super();
}
@Override
public MethodExecutor resolve(
final EvaluationContext context, final Object targetObject,
final String name, final List<TypeDescriptor> argumentTypes) throws AccessException {
final Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
// 判断对象是否时允许访问的类型
if (!ExpressionUtils.isTypeAllowed(type.getName())) {
// We will only specifically allow calling "Object.getClass()" and "Class.getName()"
if (!(Class.class.equals(type) && "getName".equals(name))
&& !(Object.class.equals(type) && "getClass".equals(name))) {
throw new EvaluationException(
String.format(
"Calling methods is forbidden for type '%s' in Thymeleaf expressions. "
"Blacklisted classes are: %s.",
type.getName(), ExpressionUtils.getBlacklist()));
}
}
return super.resolve(context, targetObject, name, argumentTypes);
}
}
- 包thymeleaf-3.0.15 org.thymeleaf.util. ExpressionUtils
public final class ExpressionUtils {
// 所有黑名单类名前缀
private static final Set<String> BLOCKED_CLASS_NAME_PREFIXES =
new HashSet<String>(Arrays.asList(
"java.lang.Runtime", "java.lang.Thread", "java.lang.Class", "java.lang.ClassLoader",
"java.lang.Runnable", "java.lang.reflect.Executable",
"java.util.concurrent.Future", "java.util.concurrent.FutureTask",
"java.util.concurrent.RunnableFuture", "java.util.concurrent.ListenableFuture",
"java.util.concurrent.Executor",
"java.sql.DriverManager"));
public static boolean isTypeAllowed(final String typeName) {
Validate.notNull(typeName, "Type name cannot be null");
final int i0 = typeName.indexOf('.');
if (i0 >= 0) {
final String package0 = typeName.substring(0, i0);
if ("java".equals(package0)) { // This is the only prefix that might be blocked
for (final String prefix : BLOCKED_CLASS_NAME_PREFIXES) {
if (typeName.startsWith(prefix)) {
return false;
}
}
}
}
return true;
}
public static List<String> getBlockedClasses() {
final List<String> blocked = new ArrayList<String>();
blocked.addAll(BLOCKED_CLASS_NAME_PREFIXES);
return blocked;
}
private ExpressionUtils() {
super();
}
}