OGNL表达式注入分析
OGNL基础依赖
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.19</version>
</dependency>OGNL三要素
[*]Expression表达式
[*]root根对象、即操作对象
[*]context上下文,用于生存对象运行的属性及值,有点类似运行环境的意思,生存了环境变量
看个例子
package org.example;
public class Tester {
public User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}package org.example;
public class User {
private String name;
private int age;
public String getName(){
return name;
}
public int getAge(){
return age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public User(String name, int age){
this.name = name;
this.age = age;
}
}package org.example;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class Main {
public static void main(String[] args) throws OgnlException {
Tester tester = new Tester();
User user = new User("F12", 20);
tester.setUser(user);
// 创建context, 设置root
OgnlContext context = new OgnlContext();
context.setRoot(tester);
// 设置表达式
String expression = "user.name";
// 解析表达式
Object ognl = Ognl.parseExpression(expression);
// 调用获取值
Object value = Ognl.getValue(ognl, context, context.getRoot());
System.out.println(value);
}
}
// 输出
F12运行以上代码就是获取org.example.Tester.user.name的值,上述我们是创建了一个tester,并且让他的user属性为一个User对象,且tester设置为root,表达式为user.name也就是获取root即tester的user属性的name属性。
OGNL语法
[*].操作符
一个例子,(#a=new java.lang.String("calc")).(@java.lang.Runtime@getRuntime().exec(#a)),也可以这样(#a=new java.lang.String("calc")),(@java.lang.Runtime@getRuntime().exec(#a)),中间的点换成逗号。可以发现它执行的方式有点类似递归,他把.前面的表达式当做结果给背面的表达式执行了这里需要注意一下#前我们用括号包裹起来了,这是为了符合语法,假如去掉那一层包裹会报错
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328164330989-188045964.png#height=354&id=O9ZQ0&originHeight=1049&originWidth=1704&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=575.7142944335938
[*]#操作符
用于调用非root对象
package org.example;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class Main {
public static void main(String[] args) throws OgnlException {
Tester tester = new Tester();
User user = new User("F12", 20);
tester.setUser(user);
// 创建context, 设置root
OgnlContext context = new OgnlContext();
// context.setRoot(tester);
context.put("user", user);
// 设置表达式
String expression = "#user.name";
// 解析表达式
Object ognl = Ognl.parseExpression(expression);
// 调用获取值
Object value = Ognl.getValue(ognl, context, context.getRoot());
System.out.println(value);
}
}
// 输出
F12用于创建Map
#{"name": "f12", "level": "noob"}
用于界说变量
如一开始的例子#a=new java.lang.String("calc"),界说了一个字符串常量
[*]@操作符
用于调用静态属性、静态方法、静态变量,如上述的@java.lang.Runtime@getRuntime().exec
OGNL版本限制
在OGNL>=3.1.25版本中设置了黑名单
public static Object invokeMethod(Object target, Method method, Object[] argsArray)
throws InvocationTargetException, IllegalAccessException
{
if (_useStricterInvocation) {
final Class methodDeclaringClass = method.getDeclaringClass();// Note: synchronized(method) call below will already NPE, so no null check.
if ( (AO_SETACCESSIBLE_REF != null && AO_SETACCESSIBLE_REF.equals(method)) ||
(AO_SETACCESSIBLE_ARR_REF != null && AO_SETACCESSIBLE_ARR_REF.equals(method)) ||
(SYS_EXIT_REF != null && SYS_EXIT_REF.equals(method)) ||
(SYS_CONSOLE_REF != null && SYS_CONSOLE_REF.equals(method)) ||
AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) ||
ClassResolver.class.isAssignableFrom(methodDeclaringClass) ||
MethodAccessor.class.isAssignableFrom(methodDeclaringClass) ||
MemberAccess.class.isAssignableFrom(methodDeclaringClass) ||
OgnlContext.class.isAssignableFrom(methodDeclaringClass) ||
Runtime.class.isAssignableFrom(methodDeclaringClass) ||
ClassLoader.class.isAssignableFrom(methodDeclaringClass) ||
ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) ||
AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass) ) {
throw new IllegalAccessException("........");
}投影与选择
OGNL 支持类似数据库当中的选择与投影功能。
[*]投影:选出集合当中的雷同属性组合成一个新的集合。语法为 collection.{XXX},XXX 就是集合中每个元素的公共属性。
[*]选择:选择就是选择出集合当中符合条件的元素组合成新的集合。语法为 collection.{Y XXX},其中 Y 是一个选择操作符,XXX 是选择用的逻辑表达式。选择操作符有 3 种:
[*]? :选择满足条件的全部元素
[*]^:选择满足条件的第一个元素
[*]$:选择满足条件的最后一个元素
User p1 = new User("name1", 11);
User p2 = new User("name2", 22);
User p3 = new User("name3", 33);
User p4 = new User("name4", 44);
Map<String, Object> context = new HashMap<String, Object>();
ArrayList<User> list = new ArrayList<User>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
context.put("list", list);
System.out.println(Ognl.getValue("#list.{age}", context, list));
//
System.out.println(Ognl.getValue("#list.{age + '-' + name}", context, list));
//
System.out.println(Ognl.getValue("#list.{? #this.age > 22}", context, list));
//
System.out.println(Ognl.getValue("#list.{^ #this.age > 22}", context, list));
//
System.out.println(Ognl.getValue("#list.{$ #this.age > 22}", context, list));
// OGNL Expression解析流程
getValue处打个断点,跟进,注意这个node的类型ASTchain在OGNL表达式中,解析和执行就是通过ASTXXXX这些方法去解析执行的,一共有ASTChain、ASTConst、ASTCtor、ASTInstanceof、ASTList、ASTMethod、ASTStaticField、ASTStaticMethod.....等多种方法,其中最根本的就是chain
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328170329371-294966700.png#height=202&id=RVtN9&originHeight=615&originWidth=1658&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=544.7142944335938
进入chain的getValue方法
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328170518195-1747616860.png#height=81&id=k2yge&originHeight=218&originWidth=1462&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=543.7142944335938
进入evaluateGetValueBody方法,这里判断context是不是const,这里并不是
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328170724562-435350516.png#height=258&id=TYTV9&originHeight=676&originWidth=1417&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=540.7142944335938
往下走进入getValueBody,获取子节点,并进入子节点的getValue方法,然后就这样一直循环
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328170907268-1899539020.png#height=148&id=YJ02M&originHeight=291&originWidth=1057&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=537.7142944335938
最后进入OgnlRuntime.callMethod
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328171103119-675651790.png#height=225&id=e8LLP&originHeight=669&originWidth=1593&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=534.7142944335938
一直往下走,这里invoke,弹出计算器
https://img2024.cnblogs.com/blog/2746479/202403/2746479-20240328171338849-13425690.png#height=212&id=gv7CQ&originHeight=637&originWidth=1586&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=527.7142944335938
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]