网站怎么做区域性优化,曲靖网站建设公司靖网站建设,网站搭建空间,网站访问pv量是指什么可能每款成熟的金融app上架前都会经过层层安全检测才能执行上架#xff0c;所以我隔三差五就能看到安全检测报告中提到的问题#xff0c;根据问题的不同级别#xff0c;处理的优先级也有所不同#xff0c;此次讲的主要是一个 “轻度问题” #xff0c;个人认为属于那种可改… 可能每款成熟的金融app上架前都会经过层层安全检测才能执行上架所以我隔三差五就能看到安全检测报告中提到的问题根据问题的不同级别处理的优先级也有所不同此次讲的主要是一个 “轻度问题” 个人认为属于那种可改不可改的状态 Tip因并未重新进行安全检测尚不确定该方式是否能解决实际提到的问题 人生处处是课堂 所遇问题自我求知setAccessible反射原理 解决过程第一阶段第二阶段 最终方案解决方式ReflectionUtilsReflectionHelper 使用方式 SecurityManager 相关思考如何规避 setAccessible 风险SecurityManager 如何给 setAccessible 授权SecurityManager 如何使用 所遇问题
漏洞描述AccessibleObject 类 允许程序员绕过 由 Java 访问说明符提供的 访问控制access control检查特别是他让程序员能够允许反射对象绕过 Java access control并反过来更改私有字段或调用私有方法、行为这些通常情况下都是不允许的 |
漏洞影响不符合安全准则绕过部分安全控制
解决建议建议应用服务器或者应用程序使用 SecurityManager的。如果存在System.getSecurityManager则该方法会必须先经过它的同意才能调用这条建议是安全中心给出的然后我全局都搜索不到SecurityManager、System.getSecurityManager起初先忽略了回头看的时候在最后补充了相关内容
触发安全风险的伪代码示例 自我求知
解决问题的第一步是明确问题的产生原因然后针对于此进行逐步解决
结论先行项目中存在类对象操作的相关代码
当编译时编译器会进行访问权限检查可以通过setAccessible方法屏蔽或者说禁用运行时访问检查
setAccessible 在安全中心给出的风险代码段中 核心风险代码为 setAccessible(true)那么有必要先了解一下此为何物 之前在 java setaccessible 用处 中看到一个简短描述提示了可能存在潜在风险 按照我看源码的猜想不论访问权限是public、private哪种setAccessible 在底层中默认应该都是 false意味着都需要接受权限检查主要区别在于 public 可以通过检查而 private 通不过 setAccessible(boolean flag) 方法是 AccessibleObject 类中的一个方法它是 Field、 Method、Constructor的公共父类。 通过反射Class类后以下三种都是其内部可反射的范围当触发这些场景将执行运行时访问检查
Field字段 设置字段set(Object obj, Object value)或获取字段get(Object obj)Method方法 调用方法invoke(Object obj, Object… args)Constructor构造函数 创建和初始化类的新实例newInstance(Object… initargs)
反射原理 Java反射是一种强大的特性它允许程序在运行时动态地获取和操作类的信息。通过反射我们可以创建对象、调用方法和访问字段而不需要提前知道类的具体定义。 Java反射的原理基于Java的运行时数据区域Runtime Data Area和类加载机制。当Java虚拟机加载一个类时它将类的字节码文件加载到内存中并在方法区创建一个Class对象来表示该类。
因为Class对象包含了类的完整信息包括类的构造函数、方法、字段等所以可以通过反射提供的一系列方法在运行时来获取Class对象、获取构造函数、获取方法、获取字段等。 解决过程 起初看到这个问题我认为是没必要解决所以直接拒了需求方然后因为工作态度就被上了一课然后直接给我发了一个别人的处理方式 - field.setAccessible(true)代码扫描有安全漏洞,解决方案 第一阶段
AccessibleObject类是 Field、Method和Constructor对象的基类能够允许反射对象修改访问权限修饰符绕过由Java访问修饰符提供的访问控制检查。它让程序员能够更改私有字段或调用私有方法这在通常情况下是不允许的
例如以下代码片段中将Field将accessible标记设置为true。 Class clazz User.class;Field field clazz.getField(name);field.setAccessible(true);如果为false则其中的私有字段不能够被访问到的所以不可以注掉。 ReflectionUtils.makeAccessible(field);个人想法起初不确定是用 ReflectionUtils.makeAccessible(field) 替换 field.setAccessible(true) 还行是在尾端加入ReflectionUtils.makeAccessible(field) 所以可以先看看源码后续会提到该类详情可以看出在源码中做了权限检查后才确定是否禁用权限检查 调用代码类似如下 Class clazz User.class;Field field clazz.getField(name);ReflectionUtils.makeAccessible(field);第二阶段
因为这里并未提供 ReflectionUtils 工具类所以自行搜索到了 ReflectionUtils反射工具精要介绍与实战应用指南 作者说org.springframework.util.ReflectionUtils 是 Spring 框架提供的一个反射工具类它封装了 Java 反射 API 的一些常用操作使得我们能够更加方便、简洁地使用反射功能… 这篇Blog内并不是一无所获至少我们可以看到这款工具类的相关调用方式 那么接了下来我们就去找一下 Android 的 ReflectionUtils 工具类 最终方案 经自我查证和同事推荐主要找到俩种方法处理方式应该是一样的但是这种方法是否真的能解决问题我目前对最终结果保持怀疑态度 解决方式
ReflectionUtils
找了半天在 Android反射机制简单理解ReflectionUtils 反射工具类 看到一个类似的 ReflectionUtils可直接copy
package xxx;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;public class ReflectionUtils {/*** Pattern for detecting CGLIB-renamed methods.* see #isCglibRenamedMethod*/private static final Pattern CGLIB_RENAMED_METHOD_PATTERN Pattern.compile(CGLIB\\$(.)\\$\\d);/*** Attempt to find a {link Field field} on the supplied {link Class} with the* supplied {code name}. Searches all superclasses up to {link Object}.* param clazz the class to introspect* param name the name of the field* return the corresponding Field object, or {code null} if not found*/public static Field findField(Class? clazz, String name) {return findField(clazz, name, null);}/*** Attempt to find a {link Field field} on the supplied {link Class} with the* supplied {code name} and/or {link Class type}. Searches all superclasses* up to {link Object}.* param clazz the class to introspect* param name the name of the field (may be {code null} if type is specified)* param type the type of the field (may be {code null} if name is specified)* return the corresponding Field object, or {code null} if not found*/public static Field findField(Class? clazz, String name, Class? type) {//Assert.notNull(clazz, Class must not be null);//Assert.isTrue(name ! null || type ! null, Either name or type of the field must be specified);Class? searchType clazz;while (!Object.class.equals(searchType) searchType ! null) {Field[] fields searchType.getDeclaredFields();for (Field field : fields) {if ((name null || name.equals(field.getName())) (type null || type.equals(field.getType()))) {return field;}}searchType searchType.getSuperclass();}return null;}/*** Set the field represented by the supplied {link Field field object} on the* specified {link Object target object} to the specified {code value}.* In accordance with {link Field#set(Object, Object)} semantics, the new value* is automatically unwrapped if the underlying field has a primitive type.* pThrown exceptions are handled via a call to {link #handleReflectionException(Exception)}.* param field the field to set* param target the target object on which to set the field* param value the value to set; may be {code null}*/public static void setField(Field field, Object target, Object value) {try {field.set(target, value);}catch (IllegalAccessException ex) {handleReflectionException(ex);throw new IllegalStateException(Unexpected reflection exception - ex.getClass().getName() : ex.getMessage());}}/*** Get the field represented by the supplied {link Field field object} on the* specified {link Object target object}. In accordance with {link Field#get(Object)}* semantics, the returned value is automatically wrapped if the underlying field* has a primitive type.* pThrown exceptions are handled via a call to {link #handleReflectionException(Exception)}.* param field the field to get* param target the target object from which to get the field* return the fields current value*/public static Object getField(Field field, Object target) {try {return field.get(target);}catch (IllegalAccessException ex) {handleReflectionException(ex);throw new IllegalStateException(Unexpected reflection exception - ex.getClass().getName() : ex.getMessage());}}/*** Attempt to find a {link Method} on the supplied class with the supplied name* and no parameters. Searches all superclasses up to {code Object}.* pReturns {code null} if no {link Method} can be found.* param clazz the class to introspect* param name the name of the method* return the Method object, or {code null} if none found*/public static Method findMethod(Class? clazz, String name) {return findMethod(clazz, name, new Class?[0]);}/*** Attempt to find a {link Method} on the supplied class with the supplied name* and parameter types. Searches all superclasses up to {code Object}.* pReturns {code null} if no {link Method} can be found.* param clazz the class to introspect* param name the name of the method* param paramTypes the parameter types of the method* (may be {code null} to indicate any signature)* return the Method object, or {code null} if none found*/public static Method findMethod(Class? clazz, String name, Class?... paramTypes) {//Assert.notNull(clazz, Class must not be null);//Assert.notNull(name, Method name must not be null);Class? searchType clazz;while (searchType ! null) {Method[] methods (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());for (Method method : methods)if (name.equals(method.getName()) (paramTypes null || Arrays.equals(paramTypes, method.getParameterTypes()))) {return method;}searchType searchType.getSuperclass();}return null;}/*** Invoke the specified {link Method} against the supplied target object with no arguments.* The target object can be {code null} when invoking a static {link Method}.* pThrown exceptions are handled via a call to {link #handleReflectionException}.* param method the method to invoke* param target the target object to invoke the method on* return the invocation result, if any* see #invokeMethod(Method, Object, Object[])*/public static Object invokeMethod(Method method, Object target) {return invokeMethod(method, target, new Object[0]);}/*** Invoke the specified {link Method} against the supplied target object with the* supplied arguments. The target object can be {code null} when invoking a* static {link Method}.* pThrown exceptions are handled via a call to {link #handleReflectionException}.* param method the method to invoke* param target the target object to invoke the method on* param args the invocation arguments (may be {code null})* return the invocation result, if any*/public static Object invokeMethod(Method method, Object target, Object... args) {try {return method.invoke(target, args);}catch (Exception ex) {handleReflectionException(ex);}throw new IllegalStateException(Should never get here);}/*** Invoke the specified JDBC API {link Method} against the supplied target* object with no arguments.* param method the method to invoke* param target the target object to invoke the method on* return the invocation result, if any* throws SQLException the JDBC API SQLException to rethrow (if any)* see #invokeJdbcMethod(Method, Object, Object[])*/public static Object invokeJdbcMethod(Method method, Object target) throws SQLException {return invokeJdbcMethod(method, target, new Object[0]);}/*** Invoke the specified JDBC API {link Method} against the supplied target* object with the supplied arguments.* param method the method to invoke* param target the target object to invoke the method on* param args the invocation arguments (may be {code null})* return the invocation result, if any* throws SQLException the JDBC API SQLException to rethrow (if any)* see #invokeMethod(Method, Object, Object[])*/public static Object invokeJdbcMethod(Method method, Object target, Object... args) throws SQLException {try {return method.invoke(target, args);}catch (IllegalAccessException ex) {handleReflectionException(ex);}catch (InvocationTargetException ex) {if (ex.getTargetException() instanceof SQLException) {throw (SQLException) ex.getTargetException();}handleInvocationTargetException(ex);}throw new IllegalStateException(Should never get here);}/*** Handle the given reflection exception. Should only be called if no* checked exception is expected to be thrown by the target method.* pThrows the underlying RuntimeException or Error in case of an* InvocationTargetException with such a root cause. Throws an* IllegalStateException with an appropriate message else.* param ex the reflection exception to handle*/public static void handleReflectionException(Exception ex) {if (ex instanceof NoSuchMethodException) {throw new IllegalStateException(Method not found: ex.getMessage());}if (ex instanceof IllegalAccessException) {throw new IllegalStateException(Could not access method: ex.getMessage());}if (ex instanceof InvocationTargetException) {handleInvocationTargetException((InvocationTargetException) ex);}if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}throw new UndeclaredThrowableException(ex);}/*** Handle the given invocation target exception. Should only be called if no* checked exception is expected to be thrown by the target method.* pThrows the underlying RuntimeException or Error in case of such a root* cause. Throws an IllegalStateException else.* param ex the invocation target exception to handle*/public static void handleInvocationTargetException(InvocationTargetException ex) {rethrowRuntimeException(ex.getTargetException());}/*** Rethrow the given {link Throwable exception}, which is presumably the* emtarget exception/em of an {link InvocationTargetException}. Should* only be called if no checked exception is expected to be thrown by the* target method.* pRethrows the underlying exception cast to an {link RuntimeException} or* {link Error} if appropriate; otherwise, throws an* {link IllegalStateException}.* param ex the exception to rethrow* throws RuntimeException the rethrown exception*/public static void rethrowRuntimeException(Throwable ex) {if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}if (ex instanceof Error) {throw (Error) ex;}throw new UndeclaredThrowableException(ex);}/*** Rethrow the given {link Throwable exception}, which is presumably the* emtarget exception/em of an {link InvocationTargetException}. Should* only be called if no checked exception is expected to be thrown by the* target method.* pRethrows the underlying exception cast to an {link Exception} or* {link Error} if appropriate; otherwise, throws an* {link IllegalStateException}.* param ex the exception to rethrow* throws Exception the rethrown exception (in case of a checked exception)*/public static void rethrowException(Throwable ex) throws Exception {if (ex instanceof Exception) {throw (Exception) ex;}if (ex instanceof Error) {throw (Error) ex;}throw new UndeclaredThrowableException(ex);}/*** Determine whether the given method explicitly declares the given* exception or one of its superclasses, which means that an exception of* that type can be propagated as-is within a reflective invocation.* param method the declaring method* param exceptionType the exception to throw* return {code true} if the exception can be thrown as-is;* {code false} if it needs to be wrapped*/public static boolean declaresException(Method method, Class? exceptionType) {//Assert.notNull(method, Method must not be null);Class?[] declaredExceptions method.getExceptionTypes();for (Class? declaredException : declaredExceptions) {if (declaredException.isAssignableFrom(exceptionType)) {return true;}}return false;}/*** Determine whether the given field is a public static final constant.* param field the field to check*/public static boolean isPublicStaticFinal(Field field) {int modifiers field.getModifiers();return (Modifier.isPublic(modifiers) Modifier.isStatic(modifiers) Modifier.isFinal(modifiers));}/*** Determine whether the given method is an equals method.* see Object#equals(Object)*/public static boolean isEqualsMethod(Method method) {if (method null || !method.getName().equals(equals)) {return false;}Class?[] paramTypes method.getParameterTypes();return (paramTypes.length 1 paramTypes[0] Object.class);}/*** Determine whether the given method is a hashCode method.* see Object#hashCode()*/public static boolean isHashCodeMethod(Method method) {return (method ! null method.getName().equals(hashCode) method.getParameterTypes().length 0);}/*** Determine whether the given method is a toString method.* see Object#toString()*/public static boolean isToStringMethod(Method method) {return (method ! null method.getName().equals(toString) method.getParameterTypes().length 0);}/*** Determine whether the given method is originally declared by {link Object}.*/public static boolean isObjectMethod(Method method) {if (method null) {return false;}try {Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes());return true;}catch (Exception ex) {return false;}}/*** Determine whether the given method is a CGLIB renamed method,* following the pattern CGLIB$methodName$0.* param renamedMethod the method to check* see //org.springframework.cglib.proxy.Enhancer#rename*/public static boolean isCglibRenamedMethod(Method renamedMethod) {return CGLIB_RENAMED_METHOD_PATTERN.matcher(renamedMethod.getName()).matches();}/*** Make the given field accessible, explicitly setting it accessible if* necessary. The {code setAccessible(true)} method is only called* when actually necessary, to avoid unnecessary conflicts with a JVM* SecurityManager (if active).* param field the field to make accessible* see Field#setAccessible*/public static void makeAccessible(Field field) {if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) ||Modifier.isFinal(field.getModifiers())) !field.isAccessible()) {field.setAccessible(true);}}/*** Make the given method accessible, explicitly setting it accessible if* necessary. The {code setAccessible(true)} method is only called* when actually necessary, to avoid unnecessary conflicts with a JVM* SecurityManager (if active).* param method the method to make accessible* see Method#setAccessible*/public static void makeAccessible(Method method) {if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) !method.isAccessible()) {method.setAccessible(true);}}/*** Make the given constructor accessible, explicitly setting it accessible* if necessary. The {code setAccessible(true)} method is only called* when actually necessary, to avoid unnecessary conflicts with a JVM* SecurityManager (if active).* param ctor the constructor to make accessible* see Constructor#setAccessible*/public static void makeAccessible(Constructor? ctor) {if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) !ctor.isAccessible()) {ctor.setAccessible(true);}}/*** Perform the given callback operation on all matching methods of the given* class and superclasses.* pThe same named method occurring on subclass and superclass will appear* twice, unless excluded by a {link MethodFilter}.* param clazz class to start looking at* param mc the callback to invoke for each method* see #doWithMethods(Class, MethodCallback, MethodFilter)*/public static void doWithMethods(Class? clazz, MethodCallback mc) throws IllegalArgumentException {doWithMethods(clazz, mc, null);}/*** Perform the given callback operation on all matching methods of the given* class and superclasses (or given interface and super-interfaces).* pThe same named method occurring on subclass and superclass will appear* twice, unless excluded by the specified {link MethodFilter}.* param clazz class to start looking at* param mc the callback to invoke for each method* param mf the filter that determines the methods to apply the callback to*/public static void doWithMethods(Class? clazz, MethodCallback mc, MethodFilter mf)throws IllegalArgumentException {// Keep backing up the inheritance hierarchy.Method[] methods clazz.getDeclaredMethods();for (Method method : methods) {if (mf ! null !mf.matches(method)) {continue;}try {mc.doWith(method);}catch (IllegalAccessException ex) {throw new IllegalStateException(Shouldnt be illegal to access method method.getName() : ex);}}if (clazz.getSuperclass() ! null) {doWithMethods(clazz.getSuperclass(), mc, mf);}else if (clazz.isInterface()) {for (Class? superIfc : clazz.getInterfaces()) {doWithMethods(superIfc, mc, mf);}}}/*** Get all declared methods on the leaf class and all superclasses. Leaf* class methods are included first.*/public static Method[] getAllDeclaredMethods(Class? leafClass) throws IllegalArgumentException {final ListMethod methods new ArrayListMethod(32);doWithMethods(leafClass, new MethodCallback() {public void doWith(Method method) {methods.add(method);}});return methods.toArray(new Method[methods.size()]);}/*** Get the unique set of declared methods on the leaf class and all superclasses. Leaf* class methods are included first and while traversing the superclass hierarchy any methods found* with signatures matching a method already included are filtered out.*/public static Method[] getUniqueDeclaredMethods(Class? leafClass) throws IllegalArgumentException {final ListMethod methods new ArrayListMethod(32);doWithMethods(leafClass, new MethodCallback() {public void doWith(Method method) {boolean knownSignature false;Method methodBeingOverriddenWithCovariantReturnType null;for (Method existingMethod : methods) {if (method.getName().equals(existingMethod.getName()) Arrays.equals(method.getParameterTypes(), existingMethod.getParameterTypes())) {// Is this a covariant return type situation?if (existingMethod.getReturnType() ! method.getReturnType() existingMethod.getReturnType().isAssignableFrom(method.getReturnType())) {methodBeingOverriddenWithCovariantReturnType existingMethod;}else {knownSignature true;}break;}}if (methodBeingOverriddenWithCovariantReturnType ! null) {methods.remove(methodBeingOverriddenWithCovariantReturnType);}if (!knownSignature !isCglibRenamedMethod(method)) {methods.add(method);}}});return methods.toArray(new Method[methods.size()]);}/*** Invoke the given callback on all fields in the target class, going up the* class hierarchy to get all declared fields.* param clazz the target class to analyze* param fc the callback to invoke for each field*/public static void doWithFields(Class? clazz, FieldCallback fc) throws IllegalArgumentException {doWithFields(clazz, fc, null);}/*** Invoke the given callback on all fields in the target class, going up the* class hierarchy to get all declared fields.* param clazz the target class to analyze* param fc the callback to invoke for each field* param ff the filter that determines the fields to apply the callback to*/public static void doWithFields(Class? clazz, FieldCallback fc, FieldFilter ff)throws IllegalArgumentException {// Keep backing up the inheritance hierarchy.Class? targetClass clazz;do {Field[] fields targetClass.getDeclaredFields();for (Field field : fields) {// Skip static and final fields.if (ff ! null !ff.matches(field)) {continue;}try {fc.doWith(field);}catch (IllegalAccessException ex) {throw new IllegalStateException(Shouldnt be illegal to access field field.getName() : ex);}}targetClass targetClass.getSuperclass();}while (targetClass ! null targetClass ! Object.class);}/*** Given the source object and the destination, which must be the same class* or a subclass, copy all fields, including inherited fields. Designed to* work on objects with public no-arg constructors.* throws IllegalArgumentException if the arguments are incompatible*/public static void shallowCopyFieldState(final Object src, final Object dest) throws IllegalArgumentException {if (src null) {throw new IllegalArgumentException(Source for field copy cannot be null);}if (dest null) {throw new IllegalArgumentException(Destination for field copy cannot be null);}if (!src.getClass().isAssignableFrom(dest.getClass())) {throw new IllegalArgumentException(Destination class [ dest.getClass().getName() ] must be same or subclass as source class [ src.getClass().getName() ]);}doWithFields(src.getClass(), new FieldCallback() {public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {makeAccessible(field);Object srcValue field.get(src);field.set(dest, srcValue);}}, COPYABLE_FIELDS);}/*** Action to take on each method.*/public interface MethodCallback {/*** Perform an operation using the given method.* param method the method to operate on*/void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;}/*** Callback optionally used to filter methods to be operated on by a method callback.*/public interface MethodFilter {/*** Determine whether the given method matches.* param method the method to check*/boolean matches(Method method);}/*** Callback interface invoked on each field in the hierarchy.*/public interface FieldCallback {/*** Perform an operation using the given field.* param field the field to operate on*/void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;}/*** Callback optionally used to filter fields to be operated on by a field callback.*/public interface FieldFilter {/*** Determine whether the given field matches.* param field the field to check*/boolean matches(Field field);}/*** Pre-built FieldFilter that matches all non-static, non-final fields.*/public static FieldFilter COPYABLE_FIELDS new FieldFilter() {public boolean matches(Field field) {return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));}};/*** Pre-built MethodFilter that matches all non-bridge methods.*/public static MethodFilter NON_BRIDGED_METHODS new MethodFilter() {public boolean matches(Method method) {return !method.isBridge();}};/*** Pre-built MethodFilter that matches all non-bridge methods* which are not declared on {code java.lang.Object}.*/public static MethodFilter USER_DECLARED_METHODS new MethodFilter() {public boolean matches(Method method) {return (!method.isBridge() method.getDeclaringClass() ! Object.class);}};
}ReflectionHelper
ReflectionHelper 是 google.gson 提供的一个类有需要的话可以引入 gson 依赖不确定copy后是否可直接使用如果有包内关联类的话最好是引入依赖
package com.google.gson.internal.reflect;import com.google.gson.JsonIOException;
import com.google.gson.internal.GsonBuildConfig;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectionHelper {private static final RecordHelper RECORD_HELPER;static {RecordHelper instance;try {// Try to construct the RecordSupportedHelper, if this fails, records are not supported on this JVM.instance new RecordSupportedHelper();} catch (NoSuchMethodException e) {instance new RecordNotSupportedHelper();}RECORD_HELPER instance;}private ReflectionHelper() {}/*** Internal implementation of making an {link AccessibleObject} accessible.** param object the object that {link AccessibleObject#setAccessible(boolean)} should be called on.* throws JsonIOException if making the object accessible fails*/public static void makeAccessible(AccessibleObject object) throws JsonIOException {try {object.setAccessible(true);} catch (Exception exception) {String description getAccessibleObjectDescription(object, false);throw new JsonIOException(Failed making description accessible; either increase its visibility or write a custom TypeAdapter for its declaring type., exception);}}/*** Returns a short string describing the {link AccessibleObject} in a human-readable way.* The result is normally shorter than {link AccessibleObject#toString()} because it omits* modifiers (e.g. {code final}) and uses simple names for constructor and method parameter* types.** param object object to describe* param uppercaseFirstLetter whether the first letter of the description should be uppercased*/public static String getAccessibleObjectDescription(AccessibleObject object, boolean uppercaseFirstLetter) {String description;if (object instanceof Field) {description field fieldToString((Field) object) ;} else if (object instanceof Method) {Method method (Method) object;StringBuilder methodSignatureBuilder new StringBuilder(method.getName());appendExecutableParameters(method, methodSignatureBuilder);String methodSignature methodSignatureBuilder.toString();description method method.getDeclaringClass().getName() # methodSignature ;} else if (object instanceof Constructor) {description constructor constructorToString((Constructor?) object) ;} else {description unknown AccessibleObject object.toString();}if (uppercaseFirstLetter Character.isLowerCase(description.charAt(0))) {description Character.toUpperCase(description.charAt(0)) description.substring(1);}return description;}/*** Creates a string representation for a field, omitting modifiers and* the field type.*/public static String fieldToString(Field field) {return field.getDeclaringClass().getName() # field.getName();}/*** Creates a string representation for a constructor.* E.g.: {code java.lang.String(char[], int, int)}*/public static String constructorToString(Constructor? constructor) {StringBuilder stringBuilder new StringBuilder(constructor.getDeclaringClass().getName());appendExecutableParameters(constructor, stringBuilder);return stringBuilder.toString();}// Note: Ideally parameter type would be java.lang.reflect.Executable, but that was added in Java 8private static void appendExecutableParameters(AccessibleObject executable, StringBuilder stringBuilder) {stringBuilder.append(();Class?[] parameters (executable instanceof Method)? ((Method) executable).getParameterTypes(): ((Constructor?) executable).getParameterTypes();for (int i 0; i parameters.length; i) {if (i 0) {stringBuilder.append(, );}stringBuilder.append(parameters[i].getSimpleName());}stringBuilder.append());}/*** Tries making the constructor accessible, returning an exception message* if this fails.** param constructor constructor to make accessible* return exception message; {code null} if successful, non-{code null} if* unsuccessful*/public static String tryMakeAccessible(Constructor? constructor) {try {constructor.setAccessible(true);return null;} catch (Exception exception) {return Failed making constructor constructorToString(constructor) accessible; either increase its visibility or write a custom InstanceCreator or TypeAdapter for// Include the message since it might contain more detailed information its declaring type: exception.getMessage();}}/** If records are supported on the JVM, this is equivalent to a call to Class.isRecord() */public static boolean isRecord(Class? raw) {return RECORD_HELPER.isRecord(raw);}public static String[] getRecordComponentNames(Class? raw) {return RECORD_HELPER.getRecordComponentNames(raw);}/** Looks up the record accessor method that corresponds to the given record field */public static Method getAccessor(Class? raw, Field field) {return RECORD_HELPER.getAccessor(raw, field);}public static T ConstructorT getCanonicalRecordConstructor(ClassT raw) {return RECORD_HELPER.getCanonicalRecordConstructor(raw);}public static RuntimeException createExceptionForUnexpectedIllegalAccess(IllegalAccessException exception) {throw new RuntimeException(Unexpected IllegalAccessException occurred (Gson GsonBuildConfig.VERSION ). Certain ReflectionAccessFilter features require Java 9 to work correctly. If you are not using ReflectionAccessFilter, report this to the Gson maintainers.,exception);}private static RuntimeException createExceptionForRecordReflectionException(ReflectiveOperationException exception) {throw new RuntimeException(Unexpected ReflectiveOperationException occurred (Gson GsonBuildConfig.VERSION ). To support Java records, reflection is utilized to read out information about records. All these invocations happens after it is established that records exist in the JVM. This exception is unexpected behavior.,exception);}/*** Internal abstraction over reflection when Records are supported.*/private abstract static class RecordHelper {abstract boolean isRecord(Class? clazz);abstract String[] getRecordComponentNames(Class? clazz);abstract T ConstructorT getCanonicalRecordConstructor(ClassT raw);public abstract Method getAccessor(Class? raw, Field field);}private static class RecordSupportedHelper extends RecordHelper {private final Method isRecord;private final Method getRecordComponents;private final Method getName;private final Method getType;private RecordSupportedHelper() throws NoSuchMethodException {isRecord Class.class.getMethod(isRecord);getRecordComponents Class.class.getMethod(getRecordComponents);// Class java.lang.reflect.RecordComponentClass? classRecordComponent getRecordComponents.getReturnType().getComponentType();getName classRecordComponent.getMethod(getName);getType classRecordComponent.getMethod(getType);}Overrideboolean isRecord(Class? raw) {try {return (boolean) isRecord.invoke(raw);} catch (ReflectiveOperationException e) {throw createExceptionForRecordReflectionException(e);}}OverrideString[] getRecordComponentNames(Class? raw) {try {Object[] recordComponents (Object[]) getRecordComponents.invoke(raw);String[] componentNames new String[recordComponents.length];for (int i 0; i recordComponents.length; i) {componentNames[i] (String) getName.invoke(recordComponents[i]);}return componentNames;} catch (ReflectiveOperationException e) {throw createExceptionForRecordReflectionException(e);}}Overridepublic T ConstructorT getCanonicalRecordConstructor(ClassT raw) {try {Object[] recordComponents (Object[]) getRecordComponents.invoke(raw);Class?[] recordComponentTypes new Class?[recordComponents.length];for (int i 0; i recordComponents.length; i) {recordComponentTypes[i] (Class?) getType.invoke(recordComponents[i]);}// Uses getDeclaredConstructor because implicit constructor has same visibility as record and might// therefore not be publicreturn raw.getDeclaredConstructor(recordComponentTypes);} catch (ReflectiveOperationException e) {throw createExceptionForRecordReflectionException(e);}}Overridepublic Method getAccessor(Class? raw, Field field) {try {// Records consists of record components, each with a unique name, a corresponding field and accessor method// with the same name. Ref.: https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10.3return raw.getMethod(field.getName());} catch (ReflectiveOperationException e) {throw createExceptionForRecordReflectionException(e);}}}/*** Instance used when records are not supported*/private static class RecordNotSupportedHelper extends RecordHelper {Overrideboolean isRecord(Class? clazz) {return false;}OverrideString[] getRecordComponentNames(Class? clazz) {throw new UnsupportedOperationException(Records are not supported on this JVM, this method should not be called);}OverrideT ConstructorT getCanonicalRecordConstructor(ClassT raw) {throw new UnsupportedOperationException(Records are not supported on this JVM, this method should not be called);}Overridepublic Method getAccessor(Class? raw, Field field) {throw new UnsupportedOperationException(Records are not supported on this JVM, this method should not be called);}}
}使用方式
仅需在原 setAccessible(true) 处采用 ReflectionHelper.makeAccessible(xxx) 或 ReflectionUtils.makeAccessible(xxx) 替换即可 SecurityManager 相关思考 在此之前我应该没了解过 SecurityManager 这次应该是首次以下主要是我的一些答疑 如何规避 setAccessible 风险
Hint伪代码其实就已经做了 try、catch 操作
SecurityManager 如何给 setAccessible 授权 我理解的根据结果来看就是不建议使用 setAccessible如果要使用就用 SecurityManager 授权但即使这样也不保证就能解决安全风险
SecurityManager 如何使用