OGNL表达式注入分析 _

OGNL基础

依赖

xml 复制代码
<dependency>
	<groupId>ognl</groupId>
	<artifactId>ognl</artifactId>
	<version>3.1.19</version>
</dependency>

OGNL三要素

  • Expression表达式
  • root根对象、即操作对象
  • context上下文,用于保存对象运行的属性及值,有点类似运行环境的意思,保存了环境变量

看个例子

typescript 复制代码
package org.example;

public class Tester {
    public User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
arduino 复制代码
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;
    }
}
ini 复制代码
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)) ,中间的点换成逗号。可以发现它执行的方式有点类似递归,他把.前面的表达式当做结果给后面的表达式执行了这里需要注意一下 # 前我们用括号包裹起来了,这是为了符合语法,假如去掉那一层包裹会报错

  • # 操作符

用于调用非root对象

ini 复制代码
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版本中设置了黑名单

csharp 复制代码
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 种:? :选择满足条件的所有元素^:选择满足条件的第一个元素$:选择满足条件的最后一个元素
ini 复制代码
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));
// [11, 22, 33, 44]
System.out.println(Ognl.getValue("#list.{age + '-' + name}", context, list));
// [11-name1, 22-name2, 33-name3, 44-name4]
System.out.println(Ognl.getValue("#list.{? #this.age > 22}", context, list));
// [org.example.User@6433a2, org.example.User@5910e440]
System.out.println(Ognl.getValue("#list.{^ #this.age > 22}", context, list));
// [org.example.User@6433a2]
System.out.println(Ognl.getValue("#list.{$ #this.age > 22}", context, list));
// [org.example.User@5910e440]

OGNL Expression解析流程

**getValue处打个断点,跟进,注意这个node的类型 ASTchain在OGNL表达式中,解析和执行就是通过 ASTXXXX**这些方法去解析执行的,一共有ASTChainASTConstASTCtorASTInstanceofASTListASTMethodASTStaticFieldASTStaticMethod.....等多种方法,其中最根本的就是chain

进入chain的getValue方法

进入**evaluateGetValueBody**方法,这里判断context是不是const,这里并不是

往下走进入getValueBody,获取子节点,并进入子节点的getValue方法,然后就这样一直循环

最后进入**OgnlRuntime.callMethod**

一直往下走,这里invoke,弹出计算器

文章转载自: F12~

原文链接: www.cnblogs.com/F12-blog/p/...

体验地址: www.jnpfsoft.com/?from=001

相关推荐
打码人的日常分享4 分钟前
企业人力资源管理,人事档案管理,绩效考核,五险一金,招聘培训,薪酬管理一体化管理系统(源码)
java·数据库·python·需求分析·规格说明书
27669582924 分钟前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
爱写代码的刚子6 分钟前
C++知识总结
java·开发语言·c++
冷琴199614 分钟前
基于java+springboot的酒店预定网站、酒店客房管理系统
java·开发语言·spring boot
daiyang123...40 分钟前
IT 行业的就业情况
java
爬山算法1 小时前
Maven(6)如何使用Maven进行项目构建?
java·maven
.生产的驴1 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
爱学的小涛1 小时前
【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解
java·开发语言·笔记·后端·nio
吹老师个人app编程教学1 小时前
详解Java中的BIO、NIO、AIO
java·开发语言·nio
爱学的小涛1 小时前
【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解
java·开发语言·笔记·后端·nio