首页 > 编程语言 >【整理】【java开发】JavaWeb之JSP、Cookie、Session(一)

【整理】【java开发】JavaWeb之JSP、Cookie、Session(一)

时间:2024-09-10 11:46:15浏览次数:1  
标签:java JavaWeb Session JSP Cookie jsp servlet javax out

一、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:ifc:forEach

JSTL标签学习

https://www.runoob.com/jsp/jsp-jstl.html

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,下面分别来介绍。

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

相关文章