From dc514b369e94d17edcaec51ce496f5fc35fc21fc Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 20 May 2020 10:13:59 -0500 Subject: [PATCH] FilterInvocation Support Default Methods on HttpServletRequest Closes gh-8566 --- .../security/web/FilterInvocation.java | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/web/src/main/java/org/springframework/security/web/FilterInvocation.java b/web/src/main/java/org/springframework/security/web/FilterInvocation.java index 871b349cf1..29b0407401 100644 --- a/web/src/main/java/org/springframework/security/web/FilterInvocation.java +++ b/web/src/main/java/org/springframework/security/web/FilterInvocation.java @@ -16,6 +16,10 @@ package org.springframework.security.web; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -231,7 +235,43 @@ class DummyRequest extends HttpServletRequestWrapper { } final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler { - public Object invoke(Object proxy, Method method, Object[] args) { + private static final float JAVA_VERSION = Float.parseFloat(System.getProperty("java.class.version", "52")); + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.isDefault()) { + return invokeDefaultMethod(proxy, method, args); + } throw new UnsupportedOperationException(method + " is not supported"); } + + private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { + if (isJdk8OrEarlier()) { + return invokeDefaultMethodForJdk8(proxy, method, args); + } + return MethodHandles.lookup() + .findSpecial( + method.getDeclaringClass(), + method.getName(), + MethodType.methodType(method.getReturnType(), new Class[0]), + method.getDeclaringClass() + ) + .bindTo(proxy) + .invokeWithArguments(args); + } + + private Object invokeDefaultMethodForJdk8(Object proxy, Method method, Object[] args) throws Throwable { + Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class); + constructor.setAccessible(true); + + Class clazz = method.getDeclaringClass(); + return constructor.newInstance(clazz) + .in(clazz) + .unreflectSpecial(method, clazz) + .bindTo(proxy) + .invokeWithArguments(args); + } + + private boolean isJdk8OrEarlier() { + return JAVA_VERSION <= 52; + } }