一、JSP介绍及原理
1.1 JSP简介
1.2 JSP简单入门
1.3 JSP原理介绍
二、JSP脚本
2.1 JSP 脚本形式
2.2 JSP EL表达式
2.3 JSP JSTL标签
三、会话跟踪技术
3.1 Cookie
3.2 Session
原创 0xNvyao 安全随笔
声明
请勿利用本公众号文章内的相关技术、工具从事非法测试,如因此造成一切不良后果与文章作者及本公众号无关!
本节继续来分享JavaWeb知识,说一说JSP和Java的会话技术(Cookie、Session)
目录:
一、JSP介绍及原理
1.1 JSP简介
摘自ChatGPT:JSP(JavaServer Pages)是一种用于构建动态Web页面的Java 技术。它允许开发人员在HTML页面中嵌入Java代码,使其更易于生成动态内容、访问数据库、执行业务逻辑等。当JSP页面被请求时,服务器会将其转换为Servlet,并最终生成HTML响应发送给客户端浏览器。JSP提供了一种简单且强大的方式来创建交互式的Web应用程序。
现在几乎没有公司会用JSP了(除了安全研究,还有P用么),更好的架构是前后端分离技术,但是为啥当初会产生JSP技术呢?我想原因不外乎:
早期互联网对动态Web页面的需求
前后段分离架构/思想还不是主流
在这个背景下,就出现了HTML+Java柔和在一起的JSP技术,这个在当时那个年代确实是一个创新和技术突破,不过随着技术的不断更新迭代,目前前后端分离架构更符合现在的软件开发模式。
1.2 JSP简单入门
编写JSP代码分为三步:
1)添加JSP依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
2)创建JSP页面
在Maven工程的webapp目录下创建jsp文件:
3)编写HTML+Java代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP入门</title>
</head>
<body>
<h1>一个JSP入门Demo</h1>
<%
System.out.println("当控制台看到这行打印,说明JSP页面中的java代码被执行了~~~");
%>
</body>
</html>
运行访问hello.jsp
看效果:
1.3 JSP原理介绍
前面JSP简介说到,当JSP页面被请求时,服务器会将其转换为Servlet,并最终生成HTML响应发送给客户端浏览器。其实这句话就是JSP的原理,不过我们要通过源码看一下:
1)找到上面写的 hello.jsp 转换后的hello_jsp.java,位于打包目录下
2)hello_jsp.java的完整代码
注意:public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/7.0.47
* Generated at: 2024-09-05 09:07:52 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write("<head>\n");
out.write(" <title>JSP入门</title>\n");
out.write("</head>\n");
out.write("<body>\n");
out.write(" <h1>一个JSP入门Demo</h1>\n");
out.write(" ");
System.out.println("当控制台看到这行打印,说明JSP页面中的java代码被执行了~~~");
out.write("\n");
out.write("</body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
3)查看org.apache.jasper.runtime.HttpJspBase源码
注意:public abstract class HttpJspBase extends HttpServlet
implements HttpJspPage
4)然后仔细看上面转换的hello_jsp.java的源码
里面也包含_jspInit()、_jspService()、_jspDestroy()三个方法,这个和Servlet接口的方法类似。
其中_jspService()是由容器生成并调用的一个方法,它是JSP页面被转换成Servlet之后的核心方法之一。
分析到这就清楚了,真正给浏览器响应的不是jsp页面,而是转换后的jsp servlet对象的_jspService() 方法,这个方法主要负责处理客户端的请求并生成响应。
在这个方法中,容器会将生成的 Servlet 代码中插入 JSP 页面中的 Java 代码段,并将整个页面编译成 Servlet 类。
当客户端请求访问这个 JSP 页面时,容器会实例化这个 Servlet 类,并调用其 _jspService() 方法来处理请求。
最后贴一张图看下hello.jsp源码和转换后的hello_jsp.java源码对比:
二、JSP脚本
2.1 JSP脚本形式
介绍JSP脚本其实重点是介绍JSP脚本里如何定义Java代码。
JSP提供了三种形式:
<%...%>:内容会直接放到_jspService()方法之中
<%=...>:内容会放到_jspService()方法的out.print()中,作为out.print()的参数
<%!...%>:内容会放到_jspService()方法之外,被类直接包含
看一下这样的Demo,分别包含上面三种JSP脚本形式:
<body>
<h1>一个JSP入门Demo</h1>
<%
System.out.println("当控制台看到这行打印,说明JSP页面中的java代码被执行了~~~");
int num = 100;
%>
<%!
String username = "jsp";
void myPrint(){
System.out.println(username);
}
%>
<%="hello"%>
<%=num%>
<%=username%>
</body>
启动项目访问一下修改后的JSP页面,然后看下转换后的hello_jsp.java文件:
2.2 JSP EL表达式
JSP EL表达式是JSP规范中的一个特性,用于简化在JSP页面中访问Java对象和数据的语法。
EL提供了一种简洁、易读的方式来访问JavaBean组件、请求参数、会话属性等,并允许在JSP页面中执行基本的表达式和操作。
举几个例子:
<body>
<%--进行算数计算--%>
${100+200} <br />
<%--访问JavaBean属性--%>
${users[1].username} <br />
<%--调用方法--%>
${users[1].getPassword()} <br />
<%--操作集合列表--%>
${users[1]} <br />
<%--EL表达式隐士对象--%>
${header} <br />
${param} <br />
<%--访问作用域对象--%>
${requestScope}
${requestScope.containsKey("users")} <br />
${requestScope.get("javax.servlet.forward.context_path")} <br />
</body>
结果:
另外值得说一下的是JSP的四大作用域,分别是page、request、session、applicaiton,从左到右作用域范围一次递增,这些作用域提供了不同级别和范围的数据共享机制,例如上面通过 req.setAttribute("users", users); 给request域设置数据。
page:当前jsp页面有效(最小)
request:当前请求有效
session:当前回话有效
application:当前应用有效(最大)
2.3 JSP JSTL标签
JSP 标准标签库(JSTL)是一组自定义标签,用于简化 JSP 页面的开发。
标签非常多,可以通过这里学习,这里只介绍下最重要的两个标签:c:if
和 c:forEach
JSTL标签学习
1)引入jstl坐标
<!--jstl标签库-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
2)添加 JSTL(JSP Standard Tag Library)核心标签库指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
# 添加下面这行指令
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>JSTL入门</title>
</head>
3)编写servlet和jsp
@WebServlet("/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("gender", 1);
req.getRequestDispatcher("jstl-demo.jsp").forward(req, resp);
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>JSTL入门</title>
</head>
<body>
<%--jstl的c:if标签用于条件判断,替换java的if else--%>
<c:if test="${gender == 1}">
男
</c:if>
<c:if test="${gender == 0}">
女
</c:if>
</body>
</html>
4)访问
5)再来看下 c:forEach
标签
特别适合对集合数据进行循环遍历,替代了jsp中繁琐的java代码
<body>
<%--jstl的c:forEach标签用于循环,替换java的foreach循环--%>
<c:forEach items="${users}" var="user">
<tr align="center">
<td>${user.username}</td>
<td>${user.password}</td>
<td>${user.address}</td>
<c:if test="${user.gender == 1}">
<td>男</td>
</c:if>
<c:if test="${user.gender == 0}">
<td>女</td>
</c:if>
<br />
</tr>
</c:forEach>
</body>
三、会话跟踪技术
在本系列第一篇文章有介绍过HTTP协议的特点,其中重要的一点是HTTP是无状态的:
在 Web 开发中,HTTP 协议是无状态的,即每个请求都是相互独立的,服务器无法直接识别来自同一用户的多个请求。
为了解决这个问题,会话跟踪技术被引入,以维护用户和服务器之间的关联状态。
会话跟踪技术的实现分为客户端会话跟踪技术Cookie和服务端会话跟踪技术Session,下面分别来介绍。
3.1 Cookie
Cookie应该都很熟悉了,是一种客户端会话跟踪技术,将数据保存到客户端,以后浏览器每次请求都携带Cookie数据进行访问,服务端来校验Cookie数据。
1)创建一个新的 Maven Module
2)编写创建 Cookie 的 Servlet
@WebServlet("/cookie1")
public class SetCookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 创建cookie对象
Cookie cookie = new Cookie("whoami", "zhangsan");
//2. 发送cookie
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
3)启动项目,浏览器查看 set-cookie
4)编写获取 Cookie 的 Servlet
@WebServlet("/cookie2")
public class GetCookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取cookie数组
Cookie[] cookies = request.getCookies();
//2. 遍历数组
for (Cookie cookie : cookies) {
//3. 获取数据
String name = cookie.getName();
if ("whoami".equals(name)) {
String value = cookie.getValue();
System.out.println(name + ":" + value);
}
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
5)访问/cookie2,查看浏览器携带的cookie
服务端正常拿到并解析了cookie:
当然cookie还有一些常用属性,这里介绍下setHttpOnly和setMaxAge:
//1. 创建cookie对象
Cookie cookie = new Cookie("whoami", "zhangsan");
cookie.setMaxAge(60 * 60 * 24); // 一天时间
cookie.setHttpOnly(true);
3.2 Session
Session是服务端会话跟踪技术,将数据保存在服务端。
客户端仅保存会话标识符(通过是一个Cookie)。
1)编写存储Session的Servlet
@WebServlet("/session1")
public class SetSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 存储数据
session.setAttribute("whoami","i am session");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
2)编写获取Session数据的Servlet
@WebServlet("/session2")
public class GetSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 获取数据
Object whoami = session.getAttribute("whoami");
System.out.println(whoami);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
3)重启服务测试
访问 /session1
访问 /session2
最后来总结一下Session的原理,前面我们提到Session会话技术是用来解决HTTP无状态的,也就是说解决多次请求之间数据共享的问题,那么思考一下上面的Session是如何解决的呢?
其实就是一句话Session是基于Cookie实现的。
在上面的SetSessionServlet.java 和 GetSessionServlet.java 中分别打印如下内容:
System.out.println("SetSessionServlet设置的session对象地址:" + session); //打印session对象内存中的地址
System.out.println("SetSessionServlet设置的session对象ID:" + session.getId()); //打印session id
然后再重启项目分别访问 /session1
和 /session2
:
通过打印内容可以知道:
1、会话的创建:
当用户第一次访问网站时,服务器会为该用户创建一个唯一的会话ID,并将相关数据存储到会话对象中
会话ID通常以Cookie的形式存储在用户的浏览器中
2、会话的跟踪:
当用户在网站上浏览不同页面时,浏览器会将包含会话ID的Cookie随每个请求发送给服务器
服务器通过解析会话ID,可以识别用户会话,并将用户的相关信息存储在会话对象中
在这个过程中,服务器通过会话ID来标识和区分不同用户的会话,确保用户在网站上的各个请求之间保持一致的状态。Session会话技术的实现通常依赖于服务器端的会话管理机制,用于存储和管理用户会话的相关数据。通过这种方式,同一个会话的用户可以在整个访问过程中保持一致的状态和体验。
以上本篇就介绍了JSP技术和会话管理技术,这些技术点会在后续的整合案例里面都实现一遍。
标签:java,JavaWeb,Session,JSP,Cookie,jsp,servlet,javax,out From: https://www.cnblogs.com/o-O-oO/p/18406138