CookieSession

Cookie&Session

  • Cookie&Session
  • 1 会话技术
    • 1.1 会话管理概述
      • 1.1.1 什么是会话
      • 1.1.2 会话管理作用
      • 1.1.3 会话管理分类
    • 1.2 客户端会话管理技术
      • 1.2.1 Cookie概述
        • 1)什么是Cookie
        • 2)Cookie的API详解
        • 3)Cookie涉及的常用方法
      • 1.2.2 Cookie的Path细节:浏览器什么时候带给服务器,什么时候不带
        • 1)需求说明
        • 2)案例目的
        • 3)案例步骤
        • 4)测试结果
        • 5)路径问题的分析及总结
    • 1.3 服务端会话管理概述
      • 1.3.1 HttpSession概述
        • 1)HttpSession对象介绍
        • 2)HttpSession的获取
        • 3)HttpSession的常用方法
      • 1.3.2 HttpSession的入门案例
        • 1)需求说明
        • 2)案例目的
        • 3)原理分析
      • 1.3.3 HttpSession的钝化和活化
    • 总结
      • 1. 会话技术
      • 2. 会话分类
      • 3. Cookie
      • 4. 案例-记录用户上次访问时间
        • 1. 步骤分析
        • 2. 案例实现
      • 5. 案例-杀死Cookie
      • 6. Session
      • 7. 验证码校验
  • 2 页面技术
    • 2.1 JSP基础
      • 2.1.1 JSP简介
      • 2.1.2 JSP和HTML以及Servlet的适用场景
      • 2.1.3 JSP简单入门
      • 2.1.4 JSP说明
        • 1)执行过程分析图
        • 2)JSP的.java文件内容分析
    • 2.2 JSP应用
      • 2.2.1 JSP语法
        • 1)Java代码块
        • 2)JSP表达式
        • 3)JSP声明
        • 4)JSP注释
        • 5)语法的示例
      • 2.2.2 JSP指令
        • 1)page指令
        • 2)include指令
        • 3)taglib指令
      • 2.2.3 JSP细节
        • 1)九大隐式对象
        • 2)PageContext对象
        • 3)四大域对象
      • 2.2.4 JSP最佳实战-MVC模型
    • 总结
      • 1. JSP指令
      • 2. MVC开发模式

Cookie&Session

1 会话技术

1.1 会话管理概述

1.1.1 什么是会话

这里的会话,指的是web开发中的一次通话过程,当打开浏览器,访问网站地址后,会话开始,当关闭浏览器(或者到了过期时间),会话结束。

举个例子:

​ 例如,你在给家人打电话,这时突然有送快递的配送员敲门,你放下电话去开门,收完快递回来后,通话还在保持中,继续说话就行了。

1.1.2 会话管理作用

什么时候会用到会话管理呢?最常见的就是购物车,当我们登录成功后,把商品加入到购物车之中,此时我们无论再浏览什么商品,当点击购物车时,那些加入的商品都仍在购物车中。

在我们的实际开发中,还有很多地方都离不开会话管理技术。比如,我们在论坛发帖,没有登录的游客身份是不允许发帖的。所以当我们登录成功后,无论我们进入哪个版块发帖,只要权限允许的情况下,服务器都会认识我们,从而让我们发帖,因为登录成功的信息一直保留在服务器端的会话中。

通过上面的两个例子,我们可以看出,它是为我们共享数据用的,并且是在不同请求间实现数据共享。也就是说,如果我们需要在多次请求间实现数据共享,就可以考虑使用会话管理技术了。

1.1.3 会话管理分类

在JavaEE的项目中,会话管理分为两类。分别是:客户端会话管理技术和服务端会话管理技术。

客户端会话管理技术

​ 它是把要共享的数据保存到了客户端(也就是浏览器端)。每次请求时,把会话信息带到服务器,从而实现多次请求的数据共享。

服务端会话管理技术

​ 它本质仍是采用客户端会话管理技术,只不过保存到客户端的是一个特殊的标识,并且把要共享的数据保存到了服务端的内存对象中。每次请求时,把这个标识带到服务器端,然后使用这个标识,找到对应的内存空间,从而实现数据共享。

1.2 客户端会话管理技术

1.2.1 Cookie概述

1)什么是Cookie

它是客户端浏览器的缓存文件,里面记录了客户浏览器访问网站的一些内容。同时,也是HTTP协议请求和响应消息头的一部分(在HTTP协议课程中,我们备注了它很重要)。

2)Cookie的API详解

作用

它可以保存客户浏览器访问网站的相关内容(需要客户端不禁用Cookie)。从而在每次访问需要同一个内容时,先从本地缓存获取,使资源共享,提高效率。

Cookie的属性

属性名称属性作用是否重要
namecookie的名称必要属性
valuecookie的值(不能是中文)必要属性
pathcookie的路径重要
domaincookie的域名重要
maxAgecookie的生存时间。重要
versioncookie的版本号。不重要
commentcookie的说明。不重要

细节

Cookie有大小,个数限制。每个网站最多只能存20个cookie,且大小不能超过4kb。同时,所有网站的cookie总数不超过300个。

当删除Cookie时,设置maxAge值为0。当不设置maxAge时,使用的是浏览器的内存,当关闭浏览器之后,cookie将丢失。设置了此值,就会保存成缓存文件(值必须是大于0的,以秒为单位)。

3)Cookie涉及的常用方法

创建Cookie

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9HJLjM8-1627536777388)(assets/Cookie的方法.png)]

/**
 * 通过指定的名称和值构造一个Cookie
 *
 * Cookie的名称必须遵循RFC 2109规范。这就意味着,它只能包含ASCII字母数字字符,
 * 不能包含逗号、分号或空格或以$字符开头。
 * 创建后无法更改cookie的名称。
 *
 * 该值可以是服务器选择发送的任何内容。
 * 它的价值可能只有服务器才感兴趣。
 * 创建之后,可以使用setValue方法更改cookie的值。
 */
public Cookie(String name, String value) {
	validation.validate(name);
	this.name = name;
	this.value = value;		
}

向浏览器添加Cookie

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jryYP0Nk-1627536777390)(assets/添加Cookie的方法.png)]

/**
 * 添加Cookie到响应中。此方法可以多次调用,用以添加多个Cookie。
 */
public void addCookie(Cookie cookie);

从服务器端获取Cookie

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OqiAmZPq-1627536777392)(assets/获取Cookie的方法.png)]

/**
 * 这是HttpServletRequest中的方法。
 * 它返回一个Cookie的数组,包含客户端随此请求发送的所有Cookie对象。
 * 如果没有符合规则的cookie,则此方法返回null。
 */
 public Cookie[] getCookies();

1.2.2 Cookie的Path细节:浏览器什么时候带给服务器,什么时候不带

1)需求说明

创建一个Cookie,设置Cookie的path,通过不同的路径访问,从而查看请求携带Cookie的情况。

2)案例目的

通过此案例的讲解,同学们可以清晰的描述出,客户浏览器何时带cookie到服务器端,何时不带。

3)案例步骤

第一步:创建JavaWeb工程

沿用第一个案例中的工程即可。

第二步:编写Servlet

/**
 * Cookie的路径问题
 * 前期准备:
 * 	1.在demo1中写一个cookie到客户端
 *  2.在demo2和demo3中分别去获取cookie
 *  	demo1的Servlet映射是   /servlet/PathQuestionDemo1
 *  	demo2的Servlet映射是   /servlet/PathQuestionDemo2
 *  	demo3的Servlet映射是   /PathQuestionDemo3
 *
 * @author 
 * @Company http://www.itheima.com
 *
 */
public class PathQuestionDemo1 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//1.创建一个Cookie
		Cookie cookie = new Cookie("pathquestion","CookiePathQuestion");
		//2.设置cookie的最大存活时间
		cookie.setMaxAge(Integer.MAX_VALUE);
		//3.把cookie发送到客户端
		response.addCookie(cookie);//setHeader("Set-Cookie","cookie的值")
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}

/**
 * 获取Cookie,名称是pathquestion
 * @author 
 * @Company http://www.itheima.com
 */
public class PathQuestionDemo2 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//1.获取所有的cookie
		Cookie[] cs = request.getCookies();
		//2.遍历cookie的数组
		for(int i=0;cs!=null && i<cs.length;i++){
			if("pathquestion".equals(cs[i].getName())){
				//找到了我们想要的cookie,输出cookie的值
				response.getWriter().write(cs[i].getValue());
				return;
			}
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}
/**
 * 获取Cookie,名称是pathquestion
 * @author 
 * @Company http://www.itheima.com
 */
public class PathQuestionDemo3 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//1.获取所有的cookie
		Cookie[] cs = request.getCookies();
		//2.遍历cookie的数组
		for(int i=0;cs!=null && i<cs.length;i++){
			if("pathquestion".equals(cs[i].getName())){
				//找到了我们想要的cookie,输出cookie的值
				response.getWriter().write(cs[i].getValue());
				return;
			}
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}

第三步:配置Servlet

<!--配置Cookie路径问题案例的Servlet-->
<servlet>
    <servlet-name>PathQuestionDemo1</servlet-name>
    <servlet-class>com.itheima.web.servlet.pathquestion.PathQuestionDemo1</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>PathQuestionDemo1</servlet-name>
    <url-pattern>/servlet/PathQuestionDemo1</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>PathQuestionDemo2</servlet-name>
    <servlet-class>com.itheima.web.servlet.pathquestion.PathQuestionDemo2</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>PathQuestionDemo2</servlet-name>
    <url-pattern>/servlet/PathQuestionDemo2</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>PathQuestionDemo3</servlet-name>
    <servlet-class>com.itheima.web.servlet.pathquestion.PathQuestionDemo3</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>PathQuestionDemo3</servlet-name>
    <url-pattern>/PathQuestionDemo3</url-pattern>
</servlet-mapping>

第四步:部署工程

沿用第一个案例中的工程部署即可。

4)测试结果

通过分别运行PathQuestionDemo1,2和3这3个Servlet,我们发现由demo1写Cookie,在demo2中可以取到,但是到了demo3中就无法获取了,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4HCMCI8v-1627536777392)(assets/案例2-1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHUZHCg0-1627536777395)(assets/案例2-2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3TJXbQp-1627536777398)(assets/案例2-3.png)]

5)路径问题的分析及总结

问题:
demo2和demo3谁能取到cookie?
答案:
demo2能取到,demo3取不到
分析:
首先,我们要知道如何确定一个cookie?
那就是使用cookie的三个属性组合:domain+path+name
这里面,同一个应用的domain是一样的,在我们的案例中都是localhost。
​ 并且,我们取的都是同一个cookie,所以name也是一样的,都是pathquestion。
​ 那么,不一样的只能是path了。但是我们没有设置过cookie的path属性,这就表明path是有默认值的。
接下来,我们打开这个cookie来看一看,在ie浏览器访问一次PathQuestionDemo1这个Servlet:

Cookie中的内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DN24ohR3-1627536777399)(assets/Cookie文件介绍.png)]

我们是通过demo1写的cookie,demo1的访问路径是: http://localhost:9090/servlet/PathQuestionDemo1
通过比较两个路径:请求资源地址和cookie的path,可以看出:cookie的path默认值是:请求资源URI,没有资源的部分(在我们的案例中,就是没有PathQuestionDemo1)。

客户端什么时候带cookie到服务器,什么时候不带?
​ 就是看请求资源URI和cookie的path比较。

请求资源URI.startWith(cookie的path) 如果返回的是true就带,如果返回的是false就不带。

​ 简单的说: 就是看谁的地址更精细

​ 比如:Cookie的path: /国家 /省份 /城市

	 	 请求资源URI	:   	  /国家			/省份														  不带
	 	 请求资源URI   :	   /国家			/省份			/城市			/区县				带

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSXP75az-1627536777401)(assets/案例2-4.png)]

在我们的案例中:

访问URLURI部分Cookie的Path是否携带Cookie能否取到Cookie
PathQuestionDemo2/servlet/PathQuestionDemo2/servlet/能取到
PathQuestionDemo3/PathQuestionDemo3/servlet/不带不能取到

1.3 服务端会话管理概述

1.3.1 HttpSession概述

1)HttpSession对象介绍

它是Servlet规范中提供的一个接口。该接口的实现由Servlet规范的实现提供商提供。我们使用的是Tomcat服务器,它对Servlet规范进行了实现,所以HttpSession接口的实现由Tomcat提供。该对象用于提供一种通过多个页面请求或访问网站来标识用户并存储有关该用户的信息的方法。简单说它就是一个服务端会话对象,用于存储用户的会话数据。

同时,它也是Servlet规范中四大域对象之一的会话域对象。并且它也是用于实现数据共享的。但它与我们之前讲解的应用域和请求域是有区别的。

域对象作用范围使用场景
ServletContext整个应用范围当前项目中需要数据共享时,可以使用此域对象。
ServletRequest当前请求范围在请求或者当前请求转发时需要数据共享可以使用此域对象。
HttpSession会话返回在当前会话范围中实现数据共享。它可以在多次请求中实现数据共享。

2)HttpSession的获取

获取HttpSession是通过HttpServletRequest接口中的两个方法获取的,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2BZeGAlu-1627536777402)(assets/获取HttpSession的两个方法.png)]

这两个方法的区别:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LW4GNTU1-1627536777404)(assets/获取Session的两个方法.png)]

3)HttpSession的常用方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27kbt9Wu-1627536777406)(assets/HttpSession方法介绍.png)]

1.3.2 HttpSession的入门案例

1)需求说明

在请求HttpSessionDemo1这个Servlet时,携带用户名信息,并且把信息保存到会话域中,然后从HttpSessionDemo2这个Servlet中获取登录信息。

2)案例目的

通过本案例的讲解,同学们可以清楚的认识到会话域的作用,即多次请求间的数据共享。因为是两次请求,请求域肯定不一样了,所以不能用请求域实现。

最终掌握HttpSession对象的获取和使用。

3)原理分析

HttpSession,它虽然是服务端会话管理技术的对象,但它本质仍是一个Cookie。是一个由服务器自动创建的特殊的Cookie,Cookie的名称就是JSESSIONID,Cookie的值是服务器分配的一个唯一的标识。

当我们使用HttpSession时,浏览器在没有禁用Cookie的情况下,都会把这个Cookie带到服务器端,然后根据唯一标识去查找对应的HttpSession对象,找到了,我们就可以直接使用了。下图就是我们入门案例中,HttpSession分配的唯一标识,同学们可以看到两次请求的JSESSIONID的值是一样的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vcQyf4f-1627536777407)(assets/案例3-5.png)]
在这里插入图片描述

1.3.3 HttpSession的钝化和活化

什么是持久态

​ 把长时间不用,但还不到过期时间的HttpSession进行序列化,写到磁盘上。

​ 我们把HttpSession持久态也叫做钝化。(与钝化相反的,我们叫活化。)

什么时候使用持久化

​ 第一种情况:当访问量很大时,服务器会根据getLastAccessTime来进行排序,对长时间不用,但是还没到过期时间的HttpSession进行持久化。

​ 第二种情况:当服务器进行重启的时候,为了保持客户HttpSession中的数据,也要对HttpSession进行持久化

注意

​ HttpSession的持久化由服务器来负责管理,我们不用关心。

​ 只有实现了序列化接口的类才能被序列化,否则不行。

总结

1. 会话技术

概念:
	当用户打开浏览器访问服务器任意资源, 会话开始了。直到其中某一方关闭了,会话就结束了。
作用:
	可以在一次会话,多次请求之间共享数据.
特点:
	保存用户在上网过程中产生的私有数据.
分类: 按照保存数据位置不同
	Cookie: 浏览器端会话技术
	Session:服务器端会话技术

2. 会话分类

3. Cookie

概念: 浏览器端会话技术
操作:
	1. 保留数据 
		Cookie new Cookie(String name,String value);
	2. 返回到浏览器
		response.addCookie(Cookie c);
	3. 获取Cookie
		Cookie[] request.getCookies();
细节:
	1. Cookie不跨浏览器
	2. 会话结束,默认Cookie销毁了.

Cookie分类
	1. 默认会话级别Cookie. 浏览器一关闭Cookie立即销毁了
	2. 持久化保存, 设置Cookie在浏览器端存活时间
		setMaxAge(int 秒);
			-1 默认,默认会话级别Cookie
			正数: 存活多少秒
			0: 立即销毁
Cookie路径:
	setPath(String path);  访问资源路径中包含path, 浏览器会自动携带Cookie发送到服务器
	例如:
		cookie.setPath("/day07");
			http://localhost:8080/day07/cookieDemo1
			http://localhost:8080/day07/cookieDemo2
			http://localhost:8080/day07/abc/cookieDemo2
			http://localhost:8080/day08/cookieDemo2
		

Cookie执行原理 (了解)

4. 案例-记录用户上次访问时间

疑问:
	1. 记录用户用户上次访问时间, 数据保存在哪儿?
	2. 保存用户私有数据
技术分析:
	Cookie

1. 步骤分析

1. 获取所有Cookie
2. 遍历数组, 查找名称为lastTime的Cookie
3. 判断Cookie是否为空
	若为空: 第一次访问, "欢迎您首次访问"
	若不空: 不是首次访问, "欢迎您回来,您上次访问时间为:xxx"
4. 使用Cookie记录当次访问时间

2. 案例实现

页面

<a href="/day07/lastTimeServlet">记录用户上次访问时间</a><br>

CookieUtils

package com.itheima.utils;

import javax.servlet.http.Cookie;

public class CookieUtils {

    /**
     * 根据名称查找Cookie
     *
     * @param cookieName
     * @param cookies
     * @return
     */
    public static Cookie findCookieByName(String cookieName, Cookie[] cookies) {
        if (cookies != null) {
            for (Cookie c : cookies) {
                // 判断有没有名称为lastTime的Cookie
                if (cookieName.equals(c.getName())) {
                    // 找到了
                    return c;
                }
            }
        }
        return null;
    }
}

LastTimeServlet

package com.itheima.a_cookie;

import com.itheima.CookieUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

@WebServlet("/lastTimeServlet")
public class LastTimeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取所有Cookie
        Cookie[] cookies = request.getCookies();
        //2. 遍历数组
        Cookie cookie = CookieUtils.findCookieByName("lastTime", cookies);
        //3. 判断cookie是否为null
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        if (cookie == null) {
            // 首次访问
            writer.write("<h2>欢迎您首次访问</h2>");
        } else {
            // 不是第一次访问
            //1. 获取Cookie值
            String time = cookie.getValue();
            writer.write("<h2>欢迎您回来, 您上次访问时间: " + time + "</h2>");
        }

        /*
            4. 使用Cookie记录当次访问时间
                注意: Cookie tomcat8以后可以存中文, 但是不能有空格
         */
        Cookie ck = new Cookie("lastTime", new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒").format(new Date()));

        // 持久化保存
        ck.setMaxAge(1000000);
        response.addCookie(ck);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

5. 案例-杀死Cookie

需求: 
	后期自动登录案例中, 用户登录成功并勾选了"自动登录",程序会使用Cookie记录用户名和密码, 而且进行了持久化保存.
	需要在退出时, 杀死Cookie, 从而实现用户的真正退出.
步骤:
	1. 创建一个同名的Cookie
	2. 设置持久化时间为 0
	3. 设置相同路径 : 不同项目可以有同名cookie
    4. 返回到浏览器, 将原来Cookie覆盖掉.

演示:


6. Session

概念:
	服务器端会话技术
作用: 
	在一次会话多次请求之间共享数据
获取对象:
	Session request.getSession();
方法:
	共享数据
        void setAttribute(String name,Object)
        Object getAttribute(String name);
        void removeAttribute(String name);
    获取标识    
		String getId();
生命周期:
	创建: 
		第一次调用request.getSession(), 一个会话只有一个Session
	销毁: 浏览器关闭,session不会立即销毁
		1. 服务器关闭了
		2. 超时销毁了. tomcat服务器webx.xml
			   <session-config>
                    <session-timeout>30</session-timeout>
                </session-config>
		3. 手动销毁: 
			session.invalidate();

思考

1. 在1中保存数据,关闭浏览器, 在2中能否获取?
	获取不到
2. 在1和2中,都执行了request.getSession(), 获取对象是否是同一个
	是

Session执行原理

Session依赖于Cookie

7. 验证码校验

目的:
	防止恶意攻击
分析:
	1.验证码需要动态生成
	2.生成验证码图片对应 验证码字符串需要存入session中
	3.验证码比较不区分大小写

验证码Servlet

package com.itheima.web.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

/**
 * 验证码
 */
@WebServlet("/checkCode")
public class CheckCodeServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
		
		//服务器通知浏览器不要缓存
		response.setHeader("pragma","no-cache");
		response.setHeader("cache-control","no-cache");
		response.setHeader("expires","0");
		
		//在内存中创建一个长80,宽30的图片,默认黑色背景
		//参数一:长
		//参数二:宽
		//参数三:颜色
		int width = 80;
		int height = 30;
		BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
		
		//获取画笔
		Graphics g = image.getGraphics();
		//设置画笔颜色为灰色
		g.setColor(Color.GRAY);
		//填充图片
		g.fillRect(0,0, width,height);
		
		//产生4个随机验证码,12Ey
		String checkCode = getCheckCode();
		//将验证码放入HttpSession中
		request.getSession().setAttribute("CHECKCODE_SERVER",checkCode);
		
		//设置画笔颜色为黄色
		g.setColor(Color.YELLOW);
		//设置字体的小大
		g.setFont(new Font("黑体",Font.BOLD,24));
		//向图片上写入验证码
		g.drawString(checkCode,15,25);
		
		//将内存中的图片输出到浏览器
		//参数一:图片对象
		//参数二:图片的格式,如PNG,JPG,GIF
		//参数三:图片输出到哪里去
		ImageIO.write(image,"PNG",response.getOutputStream());
	}
	/**
	 * 产生4位随机字符串 
	 */
	private String getCheckCode() { 
		String base = "0123456789ABCDEFGabcdefg";
		int size = base.length();
		Random r = new Random();
		StringBuffer sb = new StringBuffer();
		for(int i=1;i<=4;i++){
			//产生0到size-1的随机值
			int index = r.nextInt(size);
			//在base字符串中获取下标为index的字符
			char c = base.charAt(index);
			//将c放入到StringBuffer中去
			sb.append(c);
		}
		return sb.toString();
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request,response);
	}
}


login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form action="/day07/loginServlet2" method="post">
    <input type="text" name="code">
    <img src="/day07/checkCode"><br>
    <input type="submit">
</form>

<div style="color: red">
    ${error}
</div>
</body>
</html>


LoginServlet

package com.itheima.b_session;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/loginServlet2")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取用户输入验证码
        String code = request.getParameter("code");

        //2. 获取session验证码
        HttpSession session = request.getSession();
        String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");

        // 为了确保验证码一次性, 在获取以后需要删除session中验证码
        session.removeAttribute("CHECKCODE_SERVER");

        //3. 比较两码是否一致
        if (code.equalsIgnoreCase(checkcode_server)) {
            response.getWriter().write("success~~");
        } else {
            // 在请求域中保存错误信息  能小不大
            request.setAttribute("error", "验证码错误~");
            // 转发到login.jsp, 显示错误
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}


2 页面技术

2.1 JSP基础

2.1.1 JSP简介

JSP全称是Java Server Page,它和Servlet一样,也是sun公司推出的一套开发动态web资源的技术,称为JSP/Servlet规范。JSP的本质其实就是一个Servlet。

2.1.2 JSP和HTML以及Servlet的适用场景

类别适用场景
HTML只能开发静态资源,不能包含java代码,无法添加动态数据。
Servlet写java代码,可以输出页面内容,但是很不方便,开发效率极低。
JSP它包括了HTML的展示技术,同时具备Servlet输出动态资源的能力。但是不适合作为控制器来用。

2.1.3 JSP简单入门

创建JavaWeb工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YfH66Iva-1627536777409)(assets/案例jsp1.png)]

在index.jsp中填写内容

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>JSP的入门</title>
  </head>
  <body>
      这是第一个JSP页面
  </body>
</html>

部署项目

沿用会话管理工程的部署方式即可。

测试运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D7Rsa1Hp-1627536777411)(assets/案例jsp2.png)]

2.1.4 JSP说明

写在之前: 明确JSP就是一个Servlet。是一个特殊的Servlet。

JSP的原理:

​ 客户端提交请求

​ ——Tomcat服务器解析请求地址

​ ——找到JSP页面

​ ——Tomcat将JSP页面翻译成Servlet的java文件

​ ——将翻译好的.java文件编译成.class文件

​ ——返回到客户浏览器上。

1)执行过程分析图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ji8ElNIv-1627536777411)(assets/Tomcat执行过程.png)]

2)JSP的.java文件内容分析

当我们打开index.jsp翻译的java文件看到的就是public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase类的声明,然后我们在Tomcat的源码中找到类的声明,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rUXeu9Ss-1627536777412)(assets/Tomcat中的HttpJspBase类声明.png)]

这张图一出场,就表明我们写的JSP它本质就是一个HttpServlet了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3jozx430-1627536777414)(assets/jsp的本质说明.png)]

同时,我们在index_jsp.java文件中找到了输出页面的代码,并且在浏览器端查看源文件,看到的内容是一样的。这也就是说明,我们的浏览器上的内容,在通过jsp展示时,本质都是用out.write()输出出来的。

讲到这里,我们应该清楚的认识到,JSP它是一个特殊的Servlet,主要是用于展示动态数据。它展示的方式是用流把数据输出出来,而我们在使用JSP时,涉及HTML的部分,都与HTML的用法一致,这部分称为jsp中的模板元素,在开发过程中,先写好这些模板元素,因为它们决定了页面的外观。

2.2 JSP应用

2.2.1 JSP语法

1)Java代码块

在jsp中,可以使用java脚本代码。形式为:<% 此处写java代码 %>

但是,在实际开发中,极少使用此种形式编写java代码。同时需要注意的是:

<%
	在里面写java程序脚本需要注意:这里面的内容由tomcat负责翻译,翻译之后是service方法的成员变量
%>

示例:

<!--Java代码块-->
<% out.println("这是Java代码块");%>
<hr/>

2)JSP表达式

在jsp中,可以使用特定表达式语法,形式为:<%=表达式%>

jsp在翻译完后是out.print(表达式内容);

所以:<%out.print("当前时间);%>和<%=“当前时间”%>是一样的。

在实际开发中,这种表达式语法用的也很少使用。

示例:

<!--JSP表达式-->
<%="这是JSP表达式"%><br/>
就相当于<br/>
<%out.println("这是没有JSP表达式输出的");%>

3)JSP声明

在JSP中也可以声明一些变量,方法,静态方法,形式为:<%! 声明的内容 %>

使用JSP声明需要注意:

<%! 
	需要注意的是: 写在里面的内容将会被tomcat翻译成全局的属性或者类方法。
%>                                    

示例:

<!--JSP声明-->
<%! String str = "声明语法格式";%>
<%=str%>

4)JSP注释

在使用JSP时,它有自己的注释,形式为:<%–注释–%>

需要注意的是:

​ 在Jsp中可以使用html的注释,但是只能注释html元素,不能注释java程序片段和表达式。同时,被html注释部分会参与翻译,并且会在浏览器上显示

​ jsp的注释不仅可以注释java程序片段,也可以注释html元素,并且被jsp注释的部分不会参与翻译成.java文件,也不会在浏览器上显示。

示例:

<%--JSP注释--%>
<!--HTML注释-->

5)语法的示例

JSP语法完整示例代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JSP语法</title>
</head>
<body>

<!--Java代码块-->
<% out.println("这是Java代码块");%>
<hr/>

<!--JSP表达式-->
<%="这是JSP表达式"%><br/>
就相当于<br/>
<%out.println("这是没有JSP表达式输出的");%>

<hr/>
<!--JSP声明-->
<%! String str = "声明语法格式";%>
<%=str%>

<hr/>

<%--JSP注释--%>
<!--HTML注释-->

</body>
</html>

JSP语法运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zX4ww5aj-1627536777416)(assets/案例jsp3.png)]

2.2.2 JSP指令

1)page指令

**language:**告知引擎,脚本使用的是java,默认是java,支持java。不写也行。

extends:告知引擎,JSP对应的Servlet的父类是哪个,不需要写,也不需要改。

import:告知引擎,导入哪些包(类)。

注意:引擎会自动导入:java.lang.*,javax.servlet.*,javax.servlet.http.*,javax.servlet.jsp.*

导入的形式:

<%@page import=”java.util.Date,java.util.UUID”%>或者:

<%@page import=”java.util.Date”%>

<%@page import=”java.util.UUID”%> 用Eclipse:Alt+/ 自动导入

session:告知引擎是否产生HttpSession对象,即是否在代码中调用request.getSession()。默认是true。

buffer:JspWriter用于输出JSP内容到页面上。告知引擎,设定他的缓存大小。默认8kb。

errorPage:告知引擎,当前页面出现异常后,应该转发到哪个页面上(路径写法:/代表当前应用)

小贴士:当在errorpage上使用了isErrorPage=true之后,ie8有时候不能正常显示

配置全局错误页面:web.xml

<error-page>    
    <exception-type>java.lang.Exception</exception-type>    			
    <location>/error.jsp</location>
</error-page>
<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>                                 

当使用了全局错误页面,就无须再写errorPage来实现转到错误页面,而是由服务器负责跳转到错误页面。

isErrorPage:告知引擎,是否抓住异常。如果该属性为true,页面中就可以使用exception对象,打印异常的详细信息。默认值是false。

contentType:告知引擎,响应正文的MIME类型。contentType=“text/html;charset=UTF-8”

​ 相当于response.setContentType(“text/html;charset=UTF-8”);

pageEncoding:告知引擎,翻译jsp时(从磁盘上读取jsp文件)所用的码表。pageEncoding="UTF-8"相当于告知引擎用UTF-8读取JSP

isELIgnored*:告知引擎,是否忽略EL表达式,默认值是false,不忽略。

2)include指令

语法格式:<%@include file="" %>该指令是包含外部页面。

属性:file,以/开头,就代表当前应用。

使用示例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWHEvDfL-1627536777418)(assets/静态包含1.png)]

静态包含的特点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GyNmHxCD-1627536777420)(assets/静态包含2.png)]

3)taglib指令

语法格式:<%taglib uri="" prefix=""%>

作用:该指令用于引入外部标签库。html标签和jsp标签不用引入。

属性:

​ uri:外部标签的URI地址。

​ prefix:使用标签时的前缀。

2.2.3 JSP细节

1)九大隐式对象

什么是隐式对象呢?它指的是在jsp中,可以不声明就直接使用的对象。它只存在于jsp中,因为java类中的变量必须要先声明再使用。其实jsp中的隐式对象也并非是未声明,只是它是在翻译成.java文件时声明的。所以我们在jsp中可以直接使用。

隐式对象名称类型备注
requestjavax.servlet.http.HttpServletRequest
responsejavax.servlet.http.HttpServletResponse
sessionjavax.servlet.http.HttpSessionPage指令可以控制开关
applicationjavax.servlet.ServletContext
pageJava.lang.Object当前jsp对应的servlet引用实例
configjavax.servlet.ServletConfig
exceptionjava.lang.Throwablepage指令有开关
outjavax.servlet.jsp.JspWriter字符输出流,相当于printwriter
pageContextjavax.servlet.jsp.PageContext很重要

2)PageContext对象

简介

它是JSP独有的对象,Servlet中没有这个对象。本身也是一个域(作用范围)对象,但是它可以操作其他3个域对象中的属性。而且还可以获取其他8个隐式对象。

生命周期

它是一个局部变量,所以它的生命周期随着JSP的创建而诞生,随着JSP的结束而消失。每个JSP页面都有一个独立的PageContext。

常用方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OeJsqmRT-1627536777422)(assets/PageContext方法详解.png)]

在上图中,同学们发现没有页面域操作的方法,其实是定义在了PageContext的父类JspContext中,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzWidCjX-1627536777422)(assets/JspContext.png)]

3)四大域对象

域对象名称范围级别备注
PageContext页面范围最小,只能在当前页面用因范围太小,开发中用的很少
ServletRequest请求范围一次请求或当期请求转发用当请求转发之后,再次转发时请求域丢失
HttpSession会话范围多次请求数据共享时使用多次请求共享数据,但不同的客户端不能共享
ServletContext应用范围最大,整个应用都可以使用尽量少用,如果对数据有修改需要做同步处理

2.2.4 JSP最佳实战-MVC模型

**Servlet:**擅长处理业务逻辑,不擅长输出显示界面。在web开发中多用于控制程序逻辑(流程)。所以我们称之为:控制器。

**JSP:**擅长显示界面,不擅长处理程序逻辑。在web开发中多用于展示动态界面。所以我们称之为:视图。

例如: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aLytT8C-1627536777425)(assets/1577355748295.png)]

M:model ,通常用于封装数据,封装的是数据模型。

V:view ,通常用于展示数据。动态展示用jsp页面,静态数据展示用html。

C:controller ,通常用于处理请求和响应。一般指的是Servlet。

总结

1. JSP指令

1. page指令
	 统一配置错误码和错误页面的对应关系。在web.xml配置
        <error-page>
            <error-code>500</error-code>
            <location>/500.jsp</location>
        </error-page>

        <error-page>
            <error-code>404</error-code>
            <location>/404.jsp</location>
        </error-page>
 2.include 包含指令
	实际开发中,我们一般将页面共有的内容抽取到公共页面, 若其他页面需要将其包含过来即可.

3. taglib 引入标签库 (JSLT学习)
		prefix: 前缀, 名字任意, 建议使用c
        uri: 标签库路径
	<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

2. MVC开发模式

M: Model 模型  javaBean 封装数据
V: View  视图  JSP/HTML 显示数据
C: Controller 控制器  Servlet
	1. 获取请求参数
	2. 处理数据(封装到javaBean)
	3. 选择视图显示数据


热门文章

暂无图片
编程学习 ·

exe4j详细使用教程(附下载安装链接)

一、exe4j介绍 ​ exe4j是一个帮助你集成Java应用程序到Windows操作环境的java可执行文件生成工具&#xff0c;无论这些应用是用于服务器&#xff0c;还是图形用户界面&#xff08;GUI&#xff09;或命令行的应用程序。如果你想在任务管理器中及Windows XP分组的用户友好任务栏…
暂无图片
编程学习 ·

AUTOSAR从入门到精通100讲(126)-浅谈车载充电系统通信方案

01 引言 本文深入研究车载充电系统策略,设计出一套基于电动汽车电池管理系统与车载充电机的CAN通信协议,可供电动汽车设计人员参考借鉴。 02 电动汽车充电系统通讯网络 电动汽车整车控制系统中采用的是CAN总线通信方式,由一个整车内部高速CAN网络、内部低速CAN网络和一个充电…
暂无图片
编程学习 ·

CMake(九):生成器表达式

当运行CMake时&#xff0c;开发人员倾向于认为它是一个简单的步骤&#xff0c;需要读取项目的CMakeLists.txt文件&#xff0c;并生成相关的特定于生成器的项目文件集(例如Visual Studio解决方案和项目文件&#xff0c;Xcode项目&#xff0c;Unix Makefiles或Ninja输入文件)。然…
暂无图片
编程学习 ·

47.第十章 网络协议和管理配置 -- 网络配置(八)

4.3.3 route 命令 路由表管理命令 路由表主要构成: Destination: 目标网络ID,表示可以到达的目标网络ID,0.0.0.0/0 表示所有未知网络,又称为默认路由,优先级最低Genmask:目标网络对应的netmaskIface: 到达对应网络,应该从当前主机哪个网卡发送出来Gateway: 到达非直连的网络,…
暂无图片
编程学习 ·

元宇宙技术基础

请看图&#xff1a; 1、通过AR、VR等交互技术提升游戏的沉浸感 回顾游戏的发展历程&#xff0c;沉浸感的提升一直是技术突破的主要方向。从《愤怒的小鸟》到CSGO,游戏建模方式从2D到3D的提升使游戏中的物体呈现立体感。玩家在游戏中可以只有切换视角&#xff0c;进而提升沉浸…
暂无图片
编程学习 ·

flink的伪分布式搭建

一 flink的伪分布式搭建 1.1 执行架构图 1.Flink程序需要提交给 Job Client2.Job Client将作业提交给 Job Manager3.Job Manager负责协调资源分配和作业执行。 资源分配完成后&#xff0c;任务将提交给相应的 Task Manage。4.Task Manager启动一个线程以开始执行。Task Manage…
暂无图片
编程学习 ·

十进制正整数与二进制字符串的转换(C++)

Function one&#xff1a; //十进制数字转成二进制字符串 string Binary(int x) {string s "";while(x){if(x % 2 0) s 0 s;else s 1 s;x / 2;}return s; } Function two&#xff1a; //二进制字符串变为十进制数字 int Decimal(string s) {int num 0, …
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统&#xff1a; 微信小程序——辩论管理前台涉及技术&#xff1a;WXML 和 WXS…
暂无图片
编程学习 ·

树莓派驱动DHT11温湿度传感器

1&#xff0c;直接使用python库 代码如下 import RPi.GPIO as GPIO import dht11 import time import datetimeGPIO.setwarnings(True) GPIO.setmode(GPIO.BCM)instance dht11.DHT11(pin14)try:while True:result instance.read()if result.is_valid():print(ok)print(&quo…
暂无图片
编程学习 ·

ELK简介

ELK简介 ELK是三个开源软件的缩写&#xff0c;Elasticsearch、Logstash、Kibana。它们都是开源软件。不过现在还新增了一个 Beats&#xff0c;它是一个轻量级的日志收集处理工具(Agent)&#xff0c;Beats 占用资源少&#xff0c;适合于在各个服务器上搜集日志后传输给 Logstas…
暂无图片
编程学习 ·

Linux 基础

通常大数据框架都部署在 Linux 服务器上&#xff0c;所以需要具备一定的 Linux 知识。Linux 书籍当中比较著名的是 《鸟哥私房菜》系列&#xff0c;这个系列很全面也很经典。但如果你希望能够快速地入门&#xff0c;这里推荐《Linux 就该这么学》&#xff0c;其网站上有免费的电…
暂无图片
编程学习 ·

Windows2022 无线网卡装不上驱动

想来 Windows2022 和 windows10/11 的驱动应该差不多通用的&#xff0c;但是死活装不上呢&#xff1f; 搜一下&#xff0c;有人提到 “默认安装时‘无线LAN服务’是关闭的&#xff0c;如果需要开启&#xff0c;只需要在“添加角色和功能”中&#xff0c;选择开启“无线LAN服务…
暂无图片
编程学习 ·

【嵌入式面试宝典】版本控制工具Git常用命令总结

目录 创建仓库 查看信息 版本回退 版本检出 远程库 Git 创建仓库 git initgit add <file> 可反复多次使用&#xff0c;添加多个文件git commit -m <message> 查看信息 git status 仓库当前的状态git diff 差异对比git log 历史记录&#xff0c;提交日志--pret…
暂无图片
编程学习 ·

用Postman生成测试报告

newman newman是一款基于nodejs开发的可以运行postman脚本的工具&#xff0c;使用Newman&#xff0c;可以直接从命令运行和测试postman集合。 安装nodejs 下载地址&#xff1a;https://nodejs.org/en/download/ 选择自己系统相对应的版本内容进行下载&#xff0c;然后傻瓜式安…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…