当前位置: 首页 > news >正文

JavaWeb_第5章_会话技术_Cookie+Session

JavaWeb_第5章_会话技术_Cookie+Session

文章目录

    • JavaWeb_第5章_会话技术_Cookie+Session
    • 1,会话跟踪技术的概述
    • 2,Cookie
      • 2.1 Cookie的基本使用
      • 2.2 Cookie的原理分析
      • 2.3 Cookie的使用细节
        • 2.3.1 Cookie的存活时间
        • 2.3.2 Cookie存储中文
    • 3,Session
      • 3.1 Session的基本使用
      • 3.2 Session的原理分析
      • 3.3 Session的使用细节
        • 3.3.1 Session钝化与活化
        • 3.3.2 Session销毁
    • 4,用户登录注册案例
      • 4.1 需求分析
      • 4.2 用户登录功能
      • 4.3 记住我-设置Cookie
      • 4.4 记住我-获取Cookie
      • 4.5 用户注册功能
      • 4.6 验证码-展示
      • 4.7验证码-校验

今日目标

  • 理解什么是会话跟踪技术

  • 掌握Cookie的使用

  • 掌握Session的使用

  • 完善用户登录注册案例的功能

1,会话跟踪技术的概述

对于会话跟踪这四个词,我们需要拆开来进行解释,首先要理解什么是会话,然后再去理解什么是会话跟踪:

  • 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。

    • 从浏览器发出请求到服务端响应数据给前端之后,一次会话(在浏览器和服务器之间)就被建立了
    • 会话被建立后,如果浏览器或服务端都没有被关闭,则会话就会持续建立着
    • 浏览器和服务器就可以继续使用该会话进行请求发送和响应,上述的整个过程就被称之为会话

    用实际场景来理解下会话,比如在我们访问京东的时候,当打开浏览器进入京东首页后,浏览器和京东的服务器之间就建立了一次会话,后面的搜索商品,查看商品的详情,加入购物车等都是在这一次会话中完成。

    思考:下图中总共建立了几个会话?

在这里插入图片描述

每个浏览器都会与服务端建立了一个会话,加起来总共是3个会话。

  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据

    • 服务器会收到多个请求,这多个请求可能来自多个浏览器,如上图中的6个请求来自3个浏览器
    • 服务器需要用来识别请求是否来自同一个浏览器
    • 服务器用来识别浏览器的过程,这个过程就是会话跟踪
    • 服务器识别浏览器后就可以在同一个会话中多次请求之间来共享数据

    那么我们又有一个问题需要思考,一个会话中的多次请求为什么要共享数据呢?有了这个数据共享功能后能实现哪些功能呢?

    • 购物车: 加入购物车去购物车结算是两次请求,但是后面这次请求要想展示前一次请求所添加的商品,就需要用到数据共享。

在这里插入图片描述

  • 页面展示用户登录信息:很多网站,登录后访问多个功能发送多次请求后,浏览器上都会有当前登录用户的信息[用户名],比如百度、京东、码云等。
    在这里插入图片描述

  • 网站登录页面的记住我功能:当用户登录成功后,勾选记住我按钮后下次再登录的时候,网站就会自动填充用户名和密码,简化用户的登录操作,多次登录就会有多次请求,他们之间也涉及到共享数据

在这里插入图片描述

  • 登录页面的验证码功能:生成验证码和输入验证码点击注册这也是两次请求,这两次请求的数据之间要进行对比,相同则允许注册,不同则拒绝注册,该功能的实现也需要在同一次会话中共享数据。

在这里插入图片描述

通过这几个例子的讲解,相信大家对会话追踪技术已经有了一定的理解,该技术在实际开发中也非常重要。那么接下来我们就需要去学习下会话跟踪技术,在学习这些技术之前,我们需要思考:为什么现在浏览器和服务器不支持数据共享呢?

  • 浏览器和服务器之间使用的是HTTP请求来进行数据传输
  • HTTP协议是无状态的,每次浏览器向服务器请求时,服务器都会将该请求视为新的请求
  • HTTP协议设计成无状态的目的是让每次请求之间相互独立,互不影响
  • 请求与请求之间独立后,就无法实现多次请求之间的数据共享

分析完具体的原因后,那么该如何实现会话跟踪技术呢? 具体的实现方式有:

(1)客户端会话跟踪技术:Cookie

(2)服务端会话跟踪技术:Session

这两个技术都可以实现会话跟踪,它们之间最大的区别:Cookie是存储在浏览器端而Session是存储在服务器端

具体的学习思路为:

  • CooKie的基本使用、原理、使用细节
  • Session的基本使用、原理、使用细节
  • Cookie和Session的综合案例

小结

在这节中,我们主要介绍了下什么是会话和会话跟踪技术,需要注意的是:

  • HTTP协议是无状态的,靠HTTP协议是无法实现会话跟踪
  • 想要实现会话跟踪,就需要用到Cookie和Session

这个Cookie和Session具体该如何使用,接下来就先从Cookie来学起。

2,Cookie

学习Cookie,我们主要解决下面几个问题:

  • 什么是Cookie?
  • Cookie如何来使用?
  • Cookie是如何实现的?
  • Cookie的使用注意事项有哪些?

2.1 Cookie的基本使用

1.概念

Cookie:客户端会话技术,将数据保存到客户端,以后每次请求都携带Cookie数据进行访问。

2.Cookie的工作流程

在这里插入图片描述

  • 服务端提供了两个Servlet,分别是ServletA和ServletB
  • 浏览器发送HTTP请求1给服务端,服务端ServletA接收请求并进行业务处理
  • 服务端ServletA在处理的过程中可以创建一个Cookie对象并将name=zs的数据存入Cookie
  • 服务端ServletA在响应数据的时候,会把Cookie对象响应给浏览器
  • 浏览器接收到响应数据,会把Cookie对象中的数据存储在浏览器内存中,此时浏览器和服务端就建立了一次会话
  • 在同一次会话中浏览器再次发送HTTP请求2给服务端ServletB,浏览器会携带Cookie对象中的所有数据
  • ServletB接收到请求和数据后,就可以获取到存储在Cookie对象中的数据,这样同一个会话中的多次请求之间就实现了数据共享

3.Cookie的基本使用

对于Cookie的使用,我们更关注的应该是后台代码如何操作Cookie,对于Cookie的操作主要分两大类,本别是发送Cookie获取Cookie,对于上面这两块内容,分别该如何实现呢?

3.1 发送Cookie

  • 创建Cookie对象,并设置数据
Cookie cookie = new Cookie("key","value");
  • 发送Cookie到客户端:使用response对象
response.addCookie(cookie);

介绍完发送Cookie对应的步骤后,接下面通过一个案例来完成Cookie的发送,具体实现步骤为:

需求:在Servlet中生成Cookie对象并存入数据,然后将数据发送给浏览器

1.创建Maven项目,项目名称为cookie-demo,并在pom.xml添加依赖

2.编写Servlet类,名称为AServlet

3.在AServlet中创建Cookie对象,存入数据,发送给前端

4.启动测试,在浏览器查看Cookie对象中的值

(1)创建Maven项目cookie-demo,并在pom.xml添加依赖

<properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target>
</properties><dependencies><!--servlet--><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.2</version><scope>provided</scope></dependency><!--jstl--><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version></dependency>
</dependencies>
<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins>
</build>

(2)编写Servlet类,名称为AServlet

@WebServlet("/aServlet")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)在Servlet中创建Cookie对象,存入数据,发送给前端

@WebServlet("/aServlet")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//发送Cookie//1. 创建Cookie对象Cookie cookie = new Cookie("username","zs");//2. 发送Cookie,responseresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(4)启动测试,在浏览器查看Cookie对象中的值

访问http://localhost:8080/cookie-demo/aServlet

chrome浏览器查看Cookie的值,有两种方式,分布式:

方式一:

在这里插入图片描述

方式二:选中打开开发者工具或者 使用快捷键F12 或者 Ctrl+Shift+I

在这里插入图片描述

3.2 获取Cookie

  • 获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
  • 遍历数组,获取每一个Cookie对象:for
  • 使用Cookie对象方法获取数据
cookie.getName();
cookie.getValue();

介绍完获取Cookie对应的步骤后,接下面再通过一个案例来完成Cookie的获取,具体实现步骤为:

需求:在Servlet中获取前一个案例存入在Cookie对象中的数据

1.编写一个新Servlet类,名称为BServlet

2.在BServlet中使用request对象获取Cookie数组,遍历数组,从数据中获取指定名称对应的值

3.启动测试,在控制台打印出获取的值

(1)编写一个新Servlet类,名称为BServlet

@WebServlet("/bServlet")
public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)在BServlet中使用request对象获取Cookie数组,遍历数组,从数据中获取指定名称对应的值

@WebServlet("/bServlet")
public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取Cookie//1. 获取Cookie数组Cookie[] cookies = request.getCookies();//2. 遍历数组for (Cookie cookie : cookies) {//3. 获取数据String name = cookie.getName();if("username".equals(name)){String value = cookie.getValue();System.out.println(name+":"+value);break;}}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)启动测试,在控制台打印出获取的值

访问http://localhost:8080/cookie-demo/bServlet

在这里插入图片描述

在IDEA控制台就能看到输出的结果:
在这里插入图片描述

==思考:==测试的时候

  • 在访问AServlet和BServlet的中间把关闭浏览器,重启浏览器后访问BServlet能否获取到Cookie中的数据?

这个问题,我们会在Cookie的使用细节中讲,大家可以动手先试下。

小结

在这节中,我们主要讲解了Cookie的基本使用,包含两部分内容

  • 发送Cookie:
    • 创建Cookie对象,并设置值:Cookie cookie = new Cookie(“key”,“value”);
    • 发送Cookie到客户端使用的是Reponse对象:response.addCookie(cookie);
  • 获取Cookie:
    • 使用Request对象获取Cookie数组:Cookie[] cookies = request.getCookies();
    • 遍历数组
    • 获取数组中每个Cookie对象的值:cookie.getName()和cookie.getValue()

介绍完Cookie的基本使用之后,那么Cookie的底层到底是如何实现一次会话两次请求之间的数据共享呢?

2.2 Cookie的原理分析

对于Cookie的实现原理是基于HTTP协议的,其中设计到HTTP协议中的两个请求头信息:

  • 响应头:set-cookie
  • 请求头: cookie

在这里插入图片描述

  • 前面的案例中已经能够实现,AServlet给前端发送Cookie,BServlet从request中获取Cookie的功能
  • 对于AServlet响应数据的时候,Tomcat服务器都是基于HTTP协议来响应数据
  • 当Tomcat发现后端要返回的是一个Cookie对象之后,Tomcat就会在响应头中添加一行数据==Set-Cookie:username=zs==
  • 浏览器获取到响应结果后,从响应头中就可以获取到Set-Cookie对应值username=zs,并将数据存储在浏览器的内存中
  • 浏览器再次发送请求给BServlet的时候,浏览器会自动在请求头中添加==Cookie: username=zs==发送给服务端BServlet
  • Request对象会把请求头中cookie对应的值封装成一个个Cookie对象,最终形成一个数组
  • BServlet通过Request对象获取到Cookie[]后,就可以从中获取自己需要的数据

接下来,使用刚才的案例,把上述结论验证下:

(1)访问AServlet对应的地址http://localhost:8080/cookie-demo/aServlet

使用Chrom浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看响应头中的数据

在这里插入图片描述

(2)访问BServlet对应的地址`http://localhost:8080/cookie-demo/bServlet

使用Chrom浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看请求头中的数据

在这里插入图片描述

2.3 Cookie的使用细节

在这节我们主要讲解两个知识,第一个是Cookie的存活时间,第二个是Cookie如何存储中文,首先来学习下Cookie的存活时间。

2.3.1 Cookie的存活时间

前面让大家思考过一个问题:

在这里插入图片描述

(1)浏览器发送请求给AServlet,AServlet会响应一个存有usernanme=zs的Cookie对象给浏览器

(2)浏览器接收到响应数据将cookie存入到浏览器内存中

(3)当浏览器再次发送请求给BServlet,BServlet就可以使用Request对象获取到Cookie数据

(4)在发送请求到BServlet之前,如果把浏览器关闭再打开进行访问,BServlet能否获取到Cookie数据?

注意:浏览器关闭再打开不是指打开一个新的选显卡,而且必须是先关闭再打开,顺序不能变。

针对上面这个问题,通过演示,会发现,BServlet中无法再获取到Cookie数据,这是为什么呢?

  • 默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁

这个结论就印证了上面的演示效果,但是如果使用这种默认情况下的Cookie,有些需求就无法实现,比如:
在这里插入图片描述

上面这个网站的登录页面上有一个记住我的功能,这个功能大家都比较熟悉

  • 第一次输入用户名和密码并勾选记住我然后进行登录
  • 下次再登陆的时候,用户名和密码就会被自动填充,不需要再重新输入登录
  • 比如记住我这个功能需要记住用户名和密码一个星期,那么使用默认情况下的Cookie就会出现问题
  • 因为默认情况,浏览器一关,Cookie就会从浏览器内存中删除,对于记住我功能就无法实现

所以我们现在就遇到一个难题是如何将Cookie持久化存储?

Cookie其实已经为我们提供好了对应的API来完成这件事,这个API就是setMaxAge,

  • 设置Cookie存活时间
setMaxAge(int seconds)

参数值为:

1.正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除

2.负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁

3.零:删除对应Cookie

接下来,咱们就在AServlet中去设置Cookie的存活时间。

@WebServlet("/aServlet")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//发送Cookie//1. 创建Cookie对象Cookie cookie = new Cookie("username","zs");//设置存活时间   ,1周 7天cookie.setMaxAge(60*60*24*7); //易阅读,需程序计算//cookie.setMaxAge(604800); //不易阅读(可以使用注解弥补),程序少进行一次计算//2. 发送Cookie,responseresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

修改完代码后,启动测试,访问http://localhost:8080/cookie-demo/aServlet

  • 访问一个AServlet后,把浏览器关闭重启后,再去访问http://localhost:8080/cookie-demo/bServet,能在控制台打印出username:zs,说明Cookie没有随着浏览器关闭而被销毁
  • 通过浏览器查看Cookie的内容,会发现Cookie的相关信息

在这里插入图片描述

2.3.2 Cookie存储中文

首先,先来演示一个效果,将之前username=zs的值改成username=张三,把汉字张三存入到Cookie中,看是什么效果:

@WebServlet("/aServlet")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//发送CookieString value = "张三";Cookie cookie = new Cookie("username",value);//设置存活时间   ,1周 7天cookie.setMaxAge(60*60*24*7);//2. 发送Cookie,responseresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

启动访问测试,访问http://localhost:8080/cookie-demo/aServlet会发现浏览器会提示错误信息

在这里插入图片描述

通过上面的案例演示,我们得到一个结论:

  • Cookie不能直接存储中文

Cookie不能存储中文,但是如果有这方面的需求,这个时候该如何解决呢?

这个时候,我们可以使用之前学过的一个知识点叫URL编码,所以如果需要存储中文,就需要进行转码,具体的实现思路为:

1.在AServlet中对中文进行URL编码,采用URLEncoder.encode(),将编码后的值存入Cookie中

2.在BServlet中获取Cookie中的值,获取的值为URL编码后的值

3.将获取的值在进行URL解码,采用URLDecoder.decode(),就可以获取到对应的中文值

(1)在AServlet中对中文进行URL编码

@WebServlet("/aServlet")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//发送CookieString value = "张三";//对中文进行URL编码value = URLEncoder.encode(value, "UTF-8");System.out.println("存储数据:"+value);//将编码后的值存入Cookie中Cookie cookie = new Cookie("username",value);//设置存活时间   ,1周 7天cookie.setMaxAge(60*60*24*7);//2. 发送Cookie,responseresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)在BServlet中获取值,并对值进行解码

@WebServlet("/bServlet")
public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取Cookie//1. 获取Cookie数组Cookie[] cookies = request.getCookies();//2. 遍历数组for (Cookie cookie : cookies) {//3. 获取数据String name = cookie.getName();if("username".equals(name)){String value = cookie.getValue();//获取的是URL编码后的值 %E5%BC%A0%E4%B8%89//URL解码value = URLDecoder.decode(value,"UTF-8");System.out.println(name+":"+value);//value解码后为 张三break;}}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

至此,我们就可以将中文存入Cookie中进行使用。

小结

Cookie的使用细节中,我们讲了Cookie的存活时间存储中文:

  • 存活时间,需要掌握setMaxAage()API的使用

  • 存储中文,需要掌握URL编码和解码的使用

3,Session

Cookie已经能完成一次会话多次请求之间的数据共享,之前我们还提到过Session也可以实现,那么:

  • 什么是Session?
  • Session如何来使用?
  • Session是如何实现的?
  • Session的使用注意事项有哪些?

3.1 Session的基本使用

1.概念

Session:服务端会话跟踪技术:将数据保存到服务端。

  • Session是存储在服务端而Cookie是存储在客户端
  • 存储在客户端的数据容易被窃取和截获,存在很多不安全的因素
  • 存储在服务端的数据相比于客户端来说就更安全

2.Session的工作流程

在这里插入图片描述

  • 在服务端的AServlet获取一个Session对象,把数据存入其中
  • 在服务端的BServlet获取到相同的Session对象,从中取出数据
  • 就可以实现一次会话中多次请求之间的数据共享了
  • 现在最大的问题是如何保证AServlet和BServlet使用的是同一个Session对象(在原理分析会讲解)?

3.Session的基本使用

在JavaEE中提供了HttpSession接口,来实现一次会话的多次请求之间数据共享功能。

具体的使用步骤为:

  • 获取Session对象,使用的是request对象
HttpSession session = request.getSession();
  • Session对象提供的功能:

    • 存储数据到 session 域中

      void setAttribute(String name, Object o)
      
    • 根据 key,获取值

      Object getAttribute(String name)
      
    • 根据 key,删除该键值对

      void removeAttribute(String name)
      

介绍完Session相关的API后,接下来通过一个案例来完成对Session的使用,具体实现步骤为:

需求:在一个Servlet中往Session中存入数据,在另一个Servlet中获取Session中存入的数据

1.创建名为SessionDemo1的Servlet类

2.创建名为SessionDemo2的Servlet类

3.在SessionDemo1的方法中:获取Session对象、存储数据

4.在SessionDemo2的方法中:获取Session对象、获取数据

5.启动测试

(1)创建名为SessionDemo1的Servlet类

@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)创建名为SessionDemo2的Servlet类

@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)SessionDemo1:获取Session对象、存储数据

@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//存储到Session中//1. 获取Session对象HttpSession session = request.getSession();//2. 存储数据session.setAttribute("username","zs");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(4)SessionDemo2:获取Session对象、获取数据

@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取数据,从session中//1. 获取Session对象HttpSession session = request.getSession();//2. 获取数据Object username = session.getAttribute("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(5)启动测试,

  • 先访问http://localhost:8080/cookie-demo/demo1,将数据存入Session
  • 在访问http://localhost:8080/cookie-demo/demo2,从Session中获取数据
  • 查看控制台

在这里插入图片描述

通过案例的效果,能看到Session是能够在一次会话中两次请求之间共享数据。

小结

至此Session的基本使用就已经完成了,重点要掌握的是:

  • Session的获取

    HttpSession session = request.getSession();
    
  • Session常用方法的使用

    void setAttribute(String name, Object o)
    Object getAttribute(String name)
    

    **注意:**Session中可以存储的是一个Object类型的数据,也就是说Session中可以存储任意数据类型。

介绍完Session的基本使用之后,那么Session的底层到底是如何实现一次会话两次请求之间的数据共享呢?

3.2 Session的原理分析

  • Session是基于Cookie实现的

这句话其实不太能详细的说明Session的底层实现,接下来,咱们一步步来分析下Session的具体实现原理:

(1)前提条件

在这里插入图片描述

Session要想实现一次会话多次请求之间的数据共享,就必须要保证多次请求获取Session的对象是同一个。

那么它们是一个对象么?要验证这个结论也很简单,只需要在上面案例中的两个Servlet中分别打印下Session对象

SessionDemo1

@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//存储到Session中//1. 获取Session对象HttpSession session = request.getSession();System.out.println(session);//2. 存储数据session.setAttribute("username","zs");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

SessionDemo2

@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取数据,从session中//1. 获取Session对象HttpSession session = request.getSession();System.out.println(session);//2. 获取数据Object username = session.getAttribute("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

启动测试,分别访问

http://localhost:8080/cookie-demo/demo1

http://localhost:8080/cookie-demo/demo2

在这里插入图片描述

通过打印可以得到如下结论:

  • 两个Servlet类中获取的Session对象是同一个
  • 把demo1和demo2请求刷新多次,控制台最终打印的结果都是同一个

那么问题又来了,如果新开一个浏览器,访问demo1或者demo2,打印在控制台的Session还是同一个对象么?

在这里插入图片描述

注意:在一台电脑上演示的时候,如果是相同的浏览器必须要把浏览器全部关掉重新打开,才算新开的一个浏览器。

当然也可以使用不同的浏览器进行测试,就不需要把之前的浏览器全部关闭。

测试的结果:如果是不同浏览器或者重新打开浏览器后,打印的Session就不一样了。

所以Session实现的也是一次会话中的多次请求之间的数据共享。

那么最主要的问题就来了,Session是如何保证在一次会话中获取的Session对象是同一个呢?

在这里插入图片描述

(1)demo1在第一次获取session对象的时候,session对象会有一个唯一的标识,假如是id:10

(2)demo1在session中存入其他数据并处理完成所有业务后,需要通过Tomcat服务器响应结果给浏览器

(3)Tomcat服务器发现业务处理中使用了session对象,就会把session的唯一标识id:10当做一个cookie,添加Set-Cookie:JESSIONID=10到响应头中,并响应给浏览器

(4)浏览器接收到响应结果后,会把响应头中的coookie数据存储到浏览器的内存中

(5)浏览器在同一会话中访问demo2的时候,会把cookie中的数据按照cookie: JESSIONID=10的格式添加到请求头中并发送给服务器Tomcat

(6)demo2获取到请求后,从请求头中就读取cookie中的JSESSIONID值为10,然后就会到服务器内存中寻找id:10的session对象,如果找到了,就直接返回该对象,如果没有则新创建一个session对象

(7)关闭打开浏览器后,因为浏览器的cookie已被销毁,所以就没有JESSIONID的数据,服务端获取到的session就是一个全新的session对象

至此,Session是基于Cookie来实现的这就话,我们就解释完了,接下来通过实例来演示下:

(1)使用chrome浏览器访问http://localhost:8080/cookie-demo/demo1,打开开发者模式(F12或Ctrl+Shift+I),查看==响应头(Response Headers)==数据:

在这里插入图片描述

(2)使用chrome浏览器再次访问http://localhost:8080/cookie-demo/demo2,查看==请求头(Request Headers)==数据:

在这里插入图片描述

小结

介绍完Session的原理,我们只需要记住

  • Session是基于Cookie来实现的

3.3 Session的使用细节

这节我们会主要讲解两个知识,第一个是Session的钝化和活化,第二个是Session的销毁,首先来学习什么是Session的钝化和活化?

3.3.1 Session钝化与活化

首先需要大家思考的问题是:

  • 服务器重启后,Session中的数据是否还在?

要想回答这个问题,我们可以先看下下面这幅图,

在这里插入图片描述

(1)服务器端AServlet和BServlet共用的session对象应该是存储在服务器的内存中

(2)服务器重新启动后,内存中的数据应该是已经被释放,对象也应该都销毁了

所以session数据应该也已经不存在了。但是如果session不存在会引发什么问题呢?

举个例子说明下,

(1)用户把需要购买的商品添加到购物车,因为要实现同一个会话多次请求数据共享,所以假设把数据存入Session对象中

(2)用户正要付钱的时候接到一个电话,付钱的动作就搁浅了

(3)正在用户打电话的时候,购物网站因为某些原因需要重启

(4)重启后session数据被销毁,购物车中的商品信息也就会随之而消失

(5)用户想再次发起支付,就会出为问题

所以说对于session的数据,我们应该做到就算服务器重启了,也应该能把数据保存下来才对。

分析了这么多,那么Tomcat服务器在重启的时候,session数据到底会不会保存以及是如何保存的,我们可以通过实际案例来演示下:

注意:这里所说的关闭和启动应该要确保是正常的关闭和启动。

那如何才是正常关闭Tomcat服务器呢?

需要使用命令行的方式来启动和停止Tomcat服务器:

启动:进入到项目pom.xml所在目录,执行tomcat7:run

在这里插入图片描述

停止:在启动的命令行界面,输入ctrl+c

在这里插入图片描述

有了上述两个正常启动和关闭的方式后,接下来的测试流程是:

(1)先启动Tomcat服务器

(2)访问http://localhost:8080/cookie-demo/demo1将数据存入session中

(3)正确停止Tomcat服务器

(4)再次重新启动Tomcat服务器

(5)访问http://localhost:8080/cookie-demo/demo2 查看是否能获取到session中的数据

在这里插入图片描述

经过测试,会发现只要服务器是正常关闭和启动,session中的数据是可以被保存下来的。

那么Tomcat服务器到底是如何做到的呢?

具体的原因就是:Session的钝化和活化:

  • 钝化:在服务器正常关闭后,Tomcat会自动将Session数据写入硬盘的文件中

    • 钝化的数据路径为:项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser

在这里插入图片描述

  • 活化:再次启动服务器后,从文件中加载数据到Session中

    • 数据加载到Session中后,路径中的SESSIONS.ser文件会被删除掉

对于上述的整个过程,大家只需要了解下即可。因为所有的过程都是Tomcat自己完成的,不需要我们参与。

小结

Session的钝化和活化介绍完后,需要我们注意的是:

  • session数据存储在服务端,服务器重启后,session数据会被保存

  • 浏览器被关闭启动后,重新建立的连接就已经是一个全新的会话,获取的session数据也是一个新的对象

  • session的数据要想共享,浏览器不能关闭,所以session数据不能长期保存数据

  • cookie是存储在客户端,是可以长期保存

3.3.2 Session销毁

session的销毁会有两种方式:

  • 默认情况下,无操作,30分钟自动销毁

    • 对于这个失效时间,是可以通过配置进行修改的

      • 在项目的web.xml中配置

        <?xml version="1.0" encoding="UTF-8"?>
        <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><session-config><session-timeout>100</session-timeout></session-config>
        </web-app>
        
      • 如果没有配置,默认是30分钟,默认值是在Tomcat的web.xml配置文件中写死的

在这里插入图片描述

  • 调用Session对象的invalidate()进行销毁

    • 在SessionDemo2类中添加session销毁的方法

      @WebServlet("/demo2")
      public class SessionDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取数据,从session中//1. 获取Session对象HttpSession session = request.getSession();System.out.println(session);// 销毁session.invalidate();//2. 获取数据Object username = session.getAttribute("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
      }
      
    • 启动访问测试,先访问demo1将数据存入到session,再次访问demo2从session中获取数据

在这里插入图片描述

  • 该销毁方法一般会在用户退出的时候,需要将session销毁掉。

Cookie和Session小结

  • Cookie 和 Session 都是来完成一次会话内多次请求间数据共享的。

所需两个对象放在一块,就需要思考:

Cookie和Session的区别是什么?

Cookie和Session的应用场景分别是什么?

  • 区别:
    • 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
    • 安全性:Cookie不安全,Session安全
    • 数据大小:Cookie最大3KB,Session无大小限制
    • 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
    • 服务器性能:Cookie不占服务器资源,Session占用服务器资源
  • 应用场景:
    • 购物车:使用Cookie来存储
    • 以登录用户的名称展示:使用Session来存储
    • 记住我功能:使用Cookie来存储
    • 验证码:使用session来存储
  • 结论
    • Cookie是用来保证用户在未登录情况下的身份识别
    • Session是用来保存用户登录后的数据

介绍完Cookie和Session以后,具体用哪个还是需要根据具体的业务进行具体分析。

4,用户登录注册案例

4.1 需求分析

需求说明:

  1. 完成用户登录功能,如果用户勾选“记住用户” ,则下次访问登录页面自动填充用户名密码

  2. 完成注册功能,并实现验证码功能
    在这里插入图片描述

4.2 用户登录功能

  1. 需求:

在这里插入图片描述

  • 用户登录成功后,跳转到列表页面,并在页面上展示当前登录的用户名称
  • 用户登录失败后,跳转回登录页面,并在页面上展示对应的错误信息
  1. 实现流程分析

在这里插入图片描述

(1)前端通过表单发送请求和数据给Web层的LoginServlet

(2)在LoginServlet中接收请求和数据[用户名和密码]

(3)LoginServlet接收到请求和数据后,调用Service层完成根据用户名和密码查询用户对象

(4)在Service层需要编写UserService类,在类中实现login方法,方法中调用Dao层的UserMapper

(5)在UserMapper接口中,声明一个根据用户名和密码查询用户信息的方法

(6)Dao层把数据查询出来以后,将返回数据封装到User对象,将对象交给Service层

(7)Service层将数据返回给Web层

(8)Web层获取到User对象后,判断User对象,如果为Null,则将错误信息响应给登录页面,如果不为Null,则跳转到列表页面,并把当前登录用户的信息存入Session携带到列表页面。

  1. 具体实现

(1)完成Dao层的代码编写

(1.1)将04-资料\1. 登录注册案例\2. MyBatis环境\UserMapper.java放到com.itheima.mapper`包下:

public interface UserMapper {/*** 根据用户名和密码查询用户对象* @param username* @param password* @return*/@Select("select * from tb_user where username = #{username} and password = #{password}")User select(@Param("username") String username,@Param("password")  String password);/*** 根据用户名查询用户对象* @param username* @return*/@Select("select * from tb_user where username = #{username}")User selectByUsername(String username);/*** 添加用户* @param user*/@Insert("insert into tb_user values(null,#{username},#{password})")void add(User user);
}

(1.2)将04-资料\1. 登录注册案例\2. MyBatis环境\User.java放到com.itheima.pojo包下:

public class User {private Integer id;private String username;private String password;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}

(1.3)将04-资料\1. 登录注册案例\2. MyBatis环境\UserMapper.xml放入到resources/com/itheima/mapper`目录下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper"></mapper>

(2)完成Service层的代码编写

(2.1)在com.itheima.service包下,创建UserService类

public class UserService {//1.使用工具类获取SqlSessionFactorySqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();/*** 登录方法* @param username* @param password* @return*/public User login(String username,String password){//2. 获取SqlSessionSqlSession sqlSession = factory.openSession();//3. 获取UserMapperUserMapper mapper = sqlSession.getMapper(UserMapper.class);//4. 调用方法User user = mapper.select(username, password);//释放资源sqlSession.close();return  user;}
}

(3)完成页面和Web层的代码编写

(3.1)将04-资料\1. 登录注册案例\1. 静态页面拷贝到项目的webapp目录下:

在这里插入图片描述

(3.2)将login.html内容修改成login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>login</title><link href="css/login.css" rel="stylesheet">
</head><body>
<div id="loginDiv" style="height: 350px"><form action="/brand-demo/loginServlet" method="post" id="form"><h1 id="loginMsg">LOGIN IN</h1><div id="errorMsg">用户名或密码不正确</div><p>Username:<input id="username" name="username" type="text"></p><p>Password:<input id="password" name="password" type="password"></p><p>Remember:<input id="remember" name="remember" type="checkbox"></p><div id="subDiv"><input type="submit" class="button" value="login up"><input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;<a href="register.html">没有账号?</a></div></form>
</div>
</body>
</html>

(3.3)创建LoginServlet类

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {private UserService service = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 获取用户名和密码String username = request.getParameter("username");String password = request.getParameter("password");//2. 调用service查询User user = service.login(username, password);//3. 判断if(user != null){//登录成功,跳转到查询所有的BrandServlet//将登陆成功后的user对象,存储到sessionHttpSession session = request.getSession();session.setAttribute("user",user);String contextPath = request.getContextPath();response.sendRedirect(contextPath+"/selectAllServlet");}else {// 登录失败,// 存储错误信息到requestrequest.setAttribute("login_msg","用户名或密码错误");// 跳转到login.jsprequest.getRequestDispatcher("/login.jsp").forward(request,response);}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3.4)在brand.jsp中标签下添加欢迎当前用户的提示信息:

<h1>${user.username},欢迎您</h1>

(3.5) 修改login.jsp,将错误信息使用EL表达式来获取

修改前内容:<div id="errorMsg">用户名或密码不正确</div>
修改后内容: <div id="errorMsg">${login_msg}</div>

(4)启动,访问测试

(4.1) 进入登录页面,输入错误的用户名或密码

在这里插入图片描述

(4.2)输入正确的用户和密码信息
在这里插入图片描述

小结

  • 在LoginServlet中,将登录成功的用户数据存入session中,方法在列表页面中获取当前登录用户信息进行展示
  • 在LoginServlet中,将登录失败的错误信息存入到request中,如果存入到session中就会出现这次会话的所有请求都有登录失败的错误信息,这个是不需要的,所以不用存入到session中

4.3 记住我-设置Cookie

  1. 需求:

如果用户勾选“记住用户” ,则下次访问登陆页面自动填充用户名密码。这样可以提升用户的体验。

在这里插入图片描述

对应上面这个需求,最大的问题就是: 如何自动填充用户名和密码?

  1. 实现流程分析

因为记住我功能要实现的效果是,就算用户把浏览器关闭过几天再来访问也能自动填充,所以需要将登陆信息存入一个可以长久保存,并且能够在浏览器关闭重新启动后依然有效的地方,就是我们前面讲的Cookie,所以:

  • 将用户名和密码写入Cookie中,并且持久化存储Cookie,下次访问浏览器会自动携带Cookie

  • 在页面获取Cookie数据后,设置到用户名和密码框中

  • 何时写入Cookie?

    • 用户必须登陆成功后才需要写
    • 用户必须在登录页面勾选了记住我的复选框

在这里插入图片描述

(1)前端需要在发送请求和数据的时候,多携带一个用户是否勾选Remember的数据

(2)LoginServlet获取到数据后,调用Service完成用户名和密码的判定

(3)登录成功,并且用户在前端勾选了记住我,需要往Cookie中写入用户名和密码的数据,并设置Cookie存活时间

(4)设置成功后,将数据响应给前端

  1. 具体实现

(1)在login.jsp为复选框设置值

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>login</title><link href="css/login.css" rel="stylesheet">
</head><body>
<div id="loginDiv" style="height: 350px"><form action="/brand-demo/loginServlet" method="post" id="form"><h1 id="loginMsg">LOGIN IN</h1><div id="errorMsg">${login_msg}</div><p>Username:<input id="username" name="username" type="text"></p><p>Password:<input id="password" name="password" type="password"></p><p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p><div id="subDiv"><input type="submit" class="button" value="login up"><input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;<a href="register.html">没有账号?</a></div></form>
</div>
</body>
</html>

(2)在LoginServlet获取复选框的值并在登录成功后进行设置Cookie

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {private UserService service = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 获取用户名和密码String username = request.getParameter("username");String password = request.getParameter("password");//获取复选框数据String remember = request.getParameter("remember");//2. 调用service查询User user = service.login(username, password);//3. 判断if(user != null){//登录成功,跳转到查询所有的BrandServlet//判断用户是否勾选记住我,字符串写前面是为了避免出现空指针异常if("1".equals(remember)){//勾选了,发送Cookie//1. 创建Cookie对象Cookie c_username = new Cookie("username",username);Cookie c_password = new Cookie("password",password);// 设置Cookie的存活时间c_username.setMaxAge( 60 * 60 * 24 * 7);c_password.setMaxAge( 60 * 60 * 24 * 7);//2. 发送response.addCookie(c_username);response.addCookie(c_password);}//将登陆成功后的user对象,存储到sessionHttpSession session = request.getSession();session.setAttribute("user",user);String contextPath = request.getContextPath();response.sendRedirect(contextPath+"/selectAllServlet");}else {// 登录失败,// 存储错误信息到requestrequest.setAttribute("login_msg","用户名或密码错误");// 跳转到login.jsprequest.getRequestDispatcher("/login.jsp").forward(request,response);}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)启动访问测试,

只有当前用户名和密码输入正确,并且勾选了Remeber的复选框,在响应头中才可以看得cookie的相关数据

在这里插入图片描述

4.4 记住我-获取Cookie

  1. 需求

登录成功并勾选了Remeber后,后端返回给前端的Cookie数据就已经存储好了,接下来就需要在页面获取Cookie中的数据,并把数据设置到登录页面的用户名和密码框中。

在这里插入图片描述

如何在页面直接获取Cookie中的值呢?

  1. 实现流程分析

在页面可以使用EL表达式,${cookie.key.value}

key:指的是存储在cookie中的键名称

在这里插入图片描述

(1)在login.jsp用户名的表单输入框使用value值给表单元素添加默认值,value可以使用${cookie.username.value}

(2)在login.jsp密码的表单输入框使用value值给表单元素添加默认值,value可以使用${cookie.password.value}

  1. 具体实现

(1)修改login.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>login</title><link href="css/login.css" rel="stylesheet">
</head><body>
<div id="loginDiv" style="height: 350px"><form action="/brand-demo/loginServlet" method="post" id="form"><h1 id="loginMsg">LOGIN IN</h1><div id="errorMsg">${login_msg}</div><p>Username:<input id="username" name="username" value="${cookie.username.value}" type="text"></p><p>Password:<input id="password" name="password" value="${cookie.password.value}" type="password"></p><p>Remember:<input id="remember" name="remember" value="1" type="checkbox"></p><div id="subDiv"><input type="submit" class="button" value="login up"><input type="reset" class="button" value="reset">&nbsp;&nbsp;&nbsp;<a href="register.html">没有账号?</a></div></form>
</div>
</body>
</html>
  1. 访问测试,重新访问登录页面,就可以看得用户和密码已经被填充。

在这里插入图片描述

4.5 用户注册功能

  1. 需求
  • 注册功能:保存用户信息到数据库
  • 验证码功能
    • 展示验证码:展示验证码图片,并可以点击切换
    • 校验验证码:验证码填写不正确,则注册失败

在这里插入图片描述

  1. 实现流程分析

在这里插入图片描述

(1)前端通过表单发送请求和数据给Web层的RegisterServlet

(2)在RegisterServlet中接收请求和数据[用户名和密码]

(3)RegisterServlet接收到请求和数据后,调用Service层完成用户信息的保存

(4)在Service层需要编写UserService类,在类中实现register方法,需要判断用户是否已经存在,如果不存在,则完成用户数据的保存

(5)在UserMapper接口中,声明两个方法,一个是根据用户名查询用户信息方法,另一个是保存用户信息方法

(6)在UserService类中保存成功则返回true,失败则返回false,将数据返回给Web层

(7)Web层获取到结果后,如果返回的是true,则提示注册成功,并转发到登录页面,如果返回false则提示用户名已存在并转发到注册页面

  1. 具体实现

(1)Dao层代码参考资料中的内容完成

(2)编写Service层代码

public class UserService {//1.使用工具类获取SqlSessionFactorySqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();/*** 注册方法* @return*/public boolean register(User user){//2. 获取SqlSessionSqlSession sqlSession = factory.openSession();//3. 获取UserMapperUserMapper mapper = sqlSession.getMapper(UserMapper.class);//4. 判断用户名是否存在User u = mapper.selectByUsername(user.getUsername());if(u == null){// 用户名不存在,注册mapper.add(user);sqlSession.commit();}sqlSession.close();return u == null;}
}

(3)完成页面和Web层的代码编写

(3.1)将register.html内容修改成register.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>欢迎注册</title><link href="css/register.css" rel="stylesheet">
</head>
<body>
<div class="form-div"><div class="reg-content"><h1>欢迎注册</h1><span>已有帐号?</span> <a href="login.html">登录</a></div><form id="reg-form" action="/brand-demo/registerServlet" method="post"><table><tr><td>用户名</td><td class="inputs"><input name="username" type="text" id="username"><br><span id="username_err" class="err_msg" style="display:none">用户名不太受欢迎</span></td></tr><tr><td>密码</td><td class="inputs"><input name="password" type="password" id="password"><br><span id="password_err" class="err_msg" style="display: none">密码格式有误</span></td></tr><tr><td>验证码</td><td class="inputs"><input name="checkCode" type="text" id="checkCode"><img src="imgs/a.jpg"><a href="#" id="changeImg" >看不清?</a></td></tr></table><div class="buttons"><input value="注 册" type="submit" id="reg_btn"></div><br class="clear"></form>
</div>
</body>
</html>

(3.2)编写RegisterServlet

@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {private UserService service = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 获取用户名和密码数据String username = request.getParameter("username");String password = request.getParameter("password");User user = new User();user.setUsername(username);user.setPassword(password);//2. 调用service 注册boolean flag = service.register(user);//3. 判断注册成功与否if(flag){//注册功能,跳转登陆页面request.setAttribute("register_msg","注册成功,请登录");request.getRequestDispatcher("/login.jsp").forward(request,response);}else {//注册失败,跳转到注册页面request.setAttribute("register_msg","用户名已存在");request.getRequestDispatcher("/register.jsp").forward(request,response);}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3.3)需要在页面上展示后台返回的错误信息,需要修改register.jsp

修改前:<span id="username_err" class="err_msg" style="display:none">用户名不太受欢迎</span>
修改后:<span id="username_err" class="err_msg">${register_msg}</span>

(3.4)如果注册成功,需要把成功信息展示在登录页面,所以也需要修改login.jsp

修改前:<div id="errorMsg">${login_msg}</div>
修改后:<div id="errorMsg">${login_msg} ${register_msg}</div>

(3.5)修改login.jsp,将注册跳转地址修改为register.jsp

修改前:<a href="register.html">没有账号?</a>
修改后: <a href="register.jsp">没有账号?</a>

(3.6)启动测试,

如果是注册的用户信息已经存在:
在这里插入图片描述

如果注册的用户信息不存在,注册成功:

在这里插入图片描述

4.6 验证码-展示

  1. 需求分析

展示验证码:展示验证码图片,并可以点击切换

在这里插入图片描述

验证码的生成是通过工具类来实现的,具体的工具类参考

04-资料\1. 登录注册案例\CheckCodeUtil.java

在该工具类中编写main方法进行测试:

public static void main(String[] args) throws IOException {//生成验证码的图片位置OutputStream fos = new FileOutputStream("d://a.jpg");//checkCode为最终验证码的数据String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, fos, 4);System.out.println(checkCode);
}

生成完验证码以后,我们就可以知晓:

  • 验证码就是使用Java代码生成的一张图片
  • 验证码的作用:防止机器自动注册,攻击服务器
  1. 实现流程分析

在这里插入图片描述

(1)前端发送请求给CheckCodeServlet

(2)CheckCodeServlet接收到请求后,生成验证码图片,将图片用Reponse对象的输出流写回到前端

思考:如何将图片写回到前端浏览器呢?

(1)Java中已经有工具类生成验证码图片,测试类中只是把图片生成到磁盘上
(2)生成磁盘的过程中使用的是OutputStream流,如何把这个图片生成在页面呢?
(3)前面在将Reponse对象的时候,它有一个方法可以获取其字节输出流,getOutputStream()
(4)综上所述,我们可以把写往磁盘的流对象更好成Response的字节流,即可完成图片响应给前端

  1. 具体实现

(1)修改Register.jsp页面,将验证码的图片从后台获取

<tr><td>验证码</td><td class="inputs"><input name="checkCode" type="text" id="checkCode"><img id="checkCodeImg" src="/brand-demo/checkCodeServlet"><a href="#" id="changeImg" >看不清?</a></td>
</tr><script>document.getElementById("changeImg").onclick = function () {//路径后面添加时间戳的目的是避免浏览器进行缓存静态资源document.getElementById("checkCodeImg").src = "/brand-demo/checkCodeServlet?"+new Date().getMilliseconds();}
</script>

(2)编写CheckCodeServlet类,用来接收请求生成验证码

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 生成验证码ServletOutputStream os = response.getOutputStream();String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

4.7验证码-校验

  1. 需求
  • 判断程序生成的验证码 和 用户输入的验证码 是否一样,如果不一样,则阻止注册
  • 验证码图片访问和提交注册表单是两次请求,所以要将程序生成的验证码存入Session中

在这里插入图片描述

思考:为什么要把验证码数据存入到Session中呢?

  • 生成验证码和校验验证码是两次请求,此处就需要在一个会话的两次请求之间共享数据
  • 验证码属于安全数据类的,所以我们选中Session来存储验证码数据。
  1. 实现流程分析

在这里插入图片描述

(1)在CheckCodeServlet中生成验证码的时候,将验证码数据存入Session对象

(2)前端将验证码和注册数据提交到后台,交给RegisterServlet类

(3)RegisterServlet类接收到请求和数据后,其中就有验证码,和Session中的验证码进行对比

(4)如果一致,则完成注册,如果不一致,则提示错误信息

  1. 具体实现

(1)修改CheckCodeServlet类,将验证码存入Session对象

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 生成验证码ServletOutputStream os = response.getOutputStream();String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);// 存入SessionHttpSession session = request.getSession();session.setAttribute("checkCodeGen",checkCode);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)在RegisterServlet中,获取页面的和session对象中的验证码,进行对比

package com.itheima.web;import com.itheima.pojo.User;
import com.itheima.service.UserService;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {private UserService service = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 获取用户名和密码数据String username = request.getParameter("username");String password = request.getParameter("password");User user = new User();user.setUsername(username);user.setPassword(password);// 获取用户输入的验证码String checkCode = request.getParameter("checkCode");// 程序生成的验证码,从Session获取HttpSession session = request.getSession();String checkCodeGen = (String) session.getAttribute("checkCodeGen");// 比对if(!checkCodeGen.equalsIgnoreCase(checkCode)){request.setAttribute("register_msg","验证码错误");request.getRequestDispatcher("/register.jsp").forward(request,response);// 不允许注册return;}//2. 调用service 注册boolean flag = service.register(user);//3. 判断注册成功与否if(flag){//注册功能,跳转登陆页面request.setAttribute("register_msg","注册成功,请登录");request.getRequestDispatcher("/login.jsp").forward(request,response);}else {//注册失败,跳转到注册页面request.setAttribute("register_msg","用户名已存在");request.getRequestDispatcher("/register.jsp").forward(request,response);}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

至此,用户的注册登录功能就已经完成了。

(8)

相关文章:

ABC346 A-G 题解

ABC346 A-G题解 A题目AC Code&#xff1a;时间复杂度 B题目时间复杂度AC Code&#xff1a; C题目时间复杂度AC Code&#xff1a; D题目时间复杂度AC Code&#xff1a; E题目时间复杂度AC Code&#xff1a; F题目时间复杂度AC Code&#xff1a; G题目时间复杂度AC Code&#xff…...

约束介绍

约束介绍 在给表中插入或者更新数据时,必须满足约束,否则,操作将失败。 约束可以在创建表时规定,或者创建表后规定(使用 Alter Table 语句创建约束)。 约束分为列级和表级。 常用的约束包含:not null、unique、primary key、foreign key、check not null:指定列不能存储…...

强化基础-Java-泛型

什么是泛型&#xff1f; 泛型其实就参数化类型&#xff0c;也就是说这个类型类似一个变量是可变的。 为什么会有泛型&#xff1f; 在没有泛型之前&#xff0c;java中是通过Object来实现泛型的功能。但是这样做有下面两个缺陷&#xff1a; 1 获取值的时候必须进行强转 2 没有…...

[转帖]Nginx+Keepalived实现简单的服务高可用

https://www.cnblogs.com/xiexun/p/14604650.html 一般情况下,如果我们做小型项目,前端用一个nginx做反向代理即可,大概是这样的 image.png 但是,作为互联网项目,纯2C的话必然需要做高可用,不仅后端的Server有N个,Nginx同样需要有N个,一主N备,当有一个服务器挂掉的时…...

Spring Colud Alibaba: Spring Cloud Alibaba 是什么

文章目录 一、介绍二、特征 一、介绍 Spring Cloud Alibaba 是 Spring Cloud 的一个子项目&#xff0c;它基于阿里巴巴的开源技术和产品&#xff0c;为开发者提供了一套微服务解决方案。Spring Cloud Alibaba 提供了一系列的微服务开发工具&#xff0c;帮助开发者构建分布式应…...

股票行情数据API | 实时行情数据接口websocket接入方法

今天给大家带来一个技术干货分享,如何通过接口API订阅并接入实时行情数据源报价,它的方法与步骤 一、API地址及传参说明 支持以下产品品类: 美股、港股、A股、外汇、贵金属、商品、数字币 github: https://github.com/alltick/realtime-forex-crypto-stock-tick-finance-webs…...

JavaWeb_第5章_会话技术_Cookie+Session

JavaWeb_第5章_会话技术_CookieSession 文章目录JavaWeb_第5章_会话技术_CookieSession1&#xff0c;会话跟踪技术的概述2&#xff0c;Cookie2.1 Cookie的基本使用2.2 Cookie的原理分析2.3 Cookie的使用细节2.3.1 Cookie的存活时间2.3.2 Cookie存储中文3&#xff0c;Session3.1…...

跟着实例学Go语言(一)

本教程全面涵盖了Go语言基础的各个方面。一共80个例子&#xff0c;每个例子对应一个语言特性点&#xff0c;非常适合新人快速上手。 教程代码示例来自go by example&#xff0c;文字部分来自本人自己的理解。 本文是教程系列的第一部分&#xff0c;共计20个例子、约1万字。 目…...

数据库基础 - 数据类型、关键字、cmd中操作数据库的命令

cmd中操作数据库的命令 mysql -hlocalhost -用户名 -密码 show database&#xff1b;查询数据库中的小数据库 show 数据库名&#xff1b;查询某一个小数据库 show 表名&#xff1b;查询表的结构 exit 退出数据类型 数值类型 int &#xff1a;整形 double&#xff1a;双精度&…...

2022SDNU-ACM结训赛题解

首先感谢一下各位出题人的精心准备、验题人的辛勤付出、以及选手的积极参加 题解 Problem A 柳予欣的归来【数学】 出题人&#xff1a; bhq 没想到一血是被打完山大的牛客比赛后来结训赛玩的wyx拿走的&#xff01; 题目描述&#xff1a; 计算(∑0<d<pd−1)m(\sum_{0…...

《人类简史》笔记三—— 历史从无正义

目录 一、尽管把人人生而平等喊得震天响&#xff0c;其实还是把人分成了上下等级 二、恶性循环 三、当男人究竟有什么好的&#xff1f; 一、尽管把人人生而平等喊得震天响&#xff0c;其实还是把人分成了上下等级 古时候&#xff1a; 上等人 平民和奴隶 现在&#xff1a;…...

Python实现基于用户的协同过滤推荐算法构建电影推荐系统

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 基于用户的协同过滤推荐&#xff08;User-based CF&#xff09;的原理假设&#xff1a;跟你喜好相似的人…...

阿里巴巴专场——第322场周赛题解

目录 模拟法&#xff1a;6253.回环句 排序后模拟&#xff1a;6254. 划分技能点相等的团队 BFS&#xff1a;6255. 两个城市间路径的最小分数 BFS&#xff1a;6256. 将节点分成尽可能多的组 模拟法&#xff1a;6253.回环句 这道题直接按照题目的意思暴力模拟即可&#xff1a;…...

【机器学习】支持向量回归

有任何的书写错误、排版错误、概念错误等&#xff0c;希望大家包含指正。 在阅读本篇之前建议先学习&#xff1a; 【机器学习】支持向量机【上】硬间隔 【机器学习】支持向量机【下】软间隔与核函数 支持向量回归 支持向量回归&#xff08;support vector regression&#xf…...

Linux安装mysql

1、 查看是否已经安装 Mysql rpm -qa | grep mysql 如果你查看出来有东西&#xff0c;可以使用下面命令将其删除 rpm -e 文件名 2 、下载官方 Mysql 包 wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm 如果安装有提示&#xff1a;Cannot…...

JeecgBoot搭建(低代码)

环境安装 后端&#xff1a;JDK: 1.8 (小于11)、Maven: 3.5、MySql: 5.7、Redis: 3.2 前端&#xff1a;Node Js: 14.18 / 16、Npm: 5.6.0、Yarn: 1.21.1 、Pnpm 工具&#xff1a; IDEA、Navicat、Git、TortoiseGit拉取代码 后端&#xff1a;git clone https://gitee.com/jeecg/…...

【java】3-获取线程引用与线程的属性

1.获取线程的引用 在创建一个线程之后&#xff0c;我们很有必要去获取当前线程实例的引用&#xff0c;以便能够观察到线程的一些属性&#xff0c;或是对于当前线程进行一系列的操作 调用Thread类的静态方法currentThread&#xff0c;我们便能拿到当前线程的引用 Thread.curr…...

2022-12-04一周学习

这周基本上还是在对前端学习的更多一点&#xff0c;主要是之前没有重视vue3的学习,现在在补上来&#xff0c;学习了vue3的一些知识&#xff0c;前端的权限管理&#xff0c;设置路由守卫&#xff0c;pinia&#xff0c;还学习了redis的一些基本操作&#xff0c;之前只是照搬了别人…...

CG-34 浊度传感器 简单说明

产品概述 浊度传感器是一种智能监测水中悬浮物对光线透过时所发生的阻碍程度的仪器。允许在水中的测量点进行无人值守的操作。采用自清洗设计&#xff0c;可清除水中附着物以及气泡聚集而影响测量结果。具有优异的抗污染能力&#xff0c;即使恶劣的环境长期在线监测&#xff0c…...

跟着实例学Go语言(二)

本教程全面涵盖了Go语言基础的各个方面。一共80个例子&#xff0c;每个例子对应一个语言特性点&#xff0c;非常适合新人快速上手。 教程代码示例来自go by example&#xff0c;文字部分来自本人自己的理解。 本文是教程系列的第二部分&#xff0c;共计20个例子、约1.2万字。 …...

linux+window+macos下的JDK安装

1. Linux中安装JDK &#xff08;1&#xff09;下载Linux版本的jdk压缩包 &#xff08;2&#xff09;解压 tar -zxvf 压缩包名 例如&#xff1a; tar -zxvf jdk-8u251-linux-x64.tar.gz&#xff08;3&#xff09;在系统配置文件配置java 编辑profile配置文件 vim /etc/prof…...

DiffuSEEG:一种基于stable diffusion 的SEEG数据补全方法

目录一. 立体脑电图&#xff08;SEEG&#xff09;1.1 SEEG概念1.2 SEEG作用1.3 SEEG的适用场景1.4 操作方法一. 立体脑电图&#xff08;SEEG&#xff09; 1.1 SEEG概念 立体脑电图&#xff08;SEEG&#xff09;&#xff1a;是一种借助外科微创的方法将电极植入到大脑不同的部…...

kubernetes—Service介绍

Service介绍 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着不方便直接采用pod的ip对服务进行访问。 为了解决这个问题&#xff0c;kubernetes提供了Service资…...

AlphaFold2源码解析(7)--模型之Evoformer

AlphaFold2源码解析(7)–模型之Evoformer 这篇文章我们主要药讲解AlphaFold2的Evoformer的代码细节。 Evoformer Stack 该网络有一个双塔结构&#xff0c;在MSA堆栈中具有轴向的自我注意&#xff1b;在Pair堆栈中具有三角形的乘法更新和三角形的自我注意&#xff1b;以及外积…...

docker 安装 redis 6.0.8 cluster 实战 (3主3从) 动态扩容

这里将上篇博客搭建的3主3从 扩容为 4主4从 1. 新建两个node节点 docker run -d \ --net host \ --privileged \ --name redis-node-7 \ --log-opt max-size100m \ --log-opt max-file3 \ -v /root/docker/redis-node-7/data:/data \ redis:6.0.8 \ --cluster-enabled yes \ -…...

20221204

You are so much more than how you look. 你比你的外表更有魅力 Never give up until the fight is over. 永远不要放弃&#xff0c;要一直战斗到最后一秒。 whats done cannot be undone 覆水难收 If I was going somewhere, I was running。 如果我要去哪儿&#xff…...

CN_数据链路层流量控制@可靠的传输机制@ARP协议

文章目录流量控制技术(协议)停止-等待流量控制滑动窗口流量控制发送窗口接收窗口基本原理可靠的传输机制确认机制ACK超时重传机制RTOARQ处理差错自动重传请求ARQ协议&#x1f388;ARQ具体协议单帧ARQ协议停止-等待协议SW-ARQ例连续ARQ协议后退N帧协议(GBN)GBN多帧滑动窗口累计确…...

程序人生 | 与足球共舞的火柴人(致敬格拉利什,赋予足球更深的意义)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…...

安装ubuntu20.04, CUDA11.4, cnDNN, tensorflow, pytorch

ubuntu22.04默认python为3.11 ubuntu20.04默认python为3.8 第一步&#xff0c;分区安装系统 efi引导区, 逻辑分区&#xff0c;512M root: 主分区&#xff0c;512M swap: 逻辑分区&#xff0c;64G home:逻辑分区&#xff0c;320G /:逻辑分区, 130G&#xff0c; CUDA是安装…...

shell脚本受限执行

shell 中运行的脚本或脚本的个代码断会禁用一些正常 shell 中可以执行的命令.这是限制脚本用户的权限和最小化运行脚本导致的破坏的安全措施.受限的内容包括&#xff1a;使用 cd 命令更改工作目录. 更改环境变量$PATH, $SHELL, $BASH_ENV,或$ENV 的值. 读或更改 shell 环境选项…...

HTTP 速查手册

一、通用身份验证 # 服务端返回401&#xff0c;并告知验证类型为Basic GET 401 Unauthorized WWW-Authenticate: Basic realm"description"# 浏览器输入验证信息后&#xff0c;请求头中携带验证信息 Authorization: Basic xxxxxxxxxx二、Cookie # 服务端返回set-co…...

【计算机视觉】 摄像机标定

摄像机标定 齐次坐标 齐次坐标&#xff0c;将欧氏空间的无穷远点&#xff0c;与投影空间中有实际意义的消失点&#xff0c;建立起映射关系。 把齐次坐标转化为笛卡尔坐标的方法&#xff1a;是前面n-1个坐标分量分别除以最后一个分量即可 一些解释和性质&#xff1a; 比较好的…...

【Redis】Redis安装步骤和特性以及支持的10种数据类型(Redis专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…...

DHTMLX Diagram JavaScript/HTML5 Pro Library:5.0

Diagram — JavaScript/HTML5 Diagram Library Ω578867473 破解版DHTMLX Diagram comprises a set of interactive HTML5 UI components such as organization charts, flowcharts, decision trees, block diagrams, mind maps, etc. Consisting of nodes and connectors, di…...

GPS卫星位置解算

本文介绍了基于C语言的GPS卫星位置解算原理与程序设计。针对每个原理、公式、代码设计进行了详细讲解&#xff0c;希望能够给测绘学子们带来帮助。 参考书籍&#xff1a; 李征航 黄劲松&#xff1a;GPS测量与数据处理&#xff08;第三版&#xff09; 目录 基础原理 1&#xf…...

大数据:Sqoop 简介与安装

一、Sqoop 简介 Sqoop 是一个常用的数据迁移工具&#xff0c;主要用于在不同存储系统之间实现数据的导入与导出&#xff1a; 导入数据&#xff1a;从 MySQL&#xff0c;Oracle 等关系型数据库中导入数据到 HDFS、Hive、HBase 等分布式文件存储系统中&#xff1b; 导出数据&am…...

[附源码]计算机毕业设计文曦家教预约系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…...

HK1 BOX刷入 Armbian系统作为服务器

HK1 BOX刷入 Armbian系统作为服务器 1 安装Armbian到EMMC 硬件 HK1 BOX s905 x3 固件版本选择 Armbian_23.02.0_Aml_s905x3_bullseye_5.15.80_server_2022.12.01用usb启动&#xff0c;tf/sd有的设备不行&#xff0c;有干扰&#xff0c;有可能从TF卡无法启动系统。 用usb启…...

CEC2015:动态多目标野狗优化算法求解CEC2015(提供完整MATLAB代码,含GD、IGD、HV和SP评价指标)

一、动态多目标优化问题简介 现实世界中&#xff0c;许多优化问题不仅具有多属性&#xff0c;而且与时间相关&#xff0c;即随着时间的变化&#xff0c;优化问题本身也发生改变&#xff0c;这类问题称为动态多目标优化问题&#xff08;dynamic multi-objective optimization p…...

【蓝桥杯选拔赛真题31】python三位数组合个数 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析

目录 python三位数组合个数 一、题目要求 1、编程实现 2、输入输出...

SpringBoot项目--如何不停服更新应用?

原文网址&#xff1a;SpringBoot项目--如何不停服更新应用&#xff1f;_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Java后端项目如何不停机更新服务。 在生产环境中&#xff0c;一般都会每个服务部署多个实例。只要多于1个实例&#xff0c;就可以不停服更新应用。 不停服…...

MVVM与Vue响应式原理

Vue的响应式实现原理 MVVM M&#xff1a;模型 》data中的数据 V&#xff1a;视图 》模板 VM&#xff1a;视图模型 》Vue实例对象 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0EWNM16D-1670161519474)(C:\Users\lucas\Desktop\学习\图片\mode…...

vue和react的生命周期

vue和react的生命周期 一、Vue的生命周期二、React的生命周期2.1.类组件2.2.函数式组件一、Vue的生命周期 初始化阶段(组件创建、数据初始化)、挂载、更新、销毁 父子组件生命周期执行顺序 初次加载组件时:父beforeCreate – 父created – 父beforeMount – 子beforeCreate …...

浏览器高度兼容性

浏览器的卷去高度 1.标准模式 document.documentElement.scrollTop 2.非标准模式 document.body.scrollTop 浏览器高度兼容性 1.标准模式下 浏览器的实际高度&#xff1a; document.body.clientHeight 浏览器的可视高度&#xff1a;document.documentElement.clientHeight 2.非…...

关于天干地支及其计算

以天干地支计算日期是我国悠良的传统文化&#xff0c;最近在看如何计算人的生辰八字&#xff0c;写了个程序&#xff0c;但是只能算年的干支&#xff0c;月、日的干支计算方法太复杂了&#xff0c;望之只能却步&#xff0c;还是乖乖去查万年历比较好。这里记下关于干支的一些东…...

数据结构和算法之如何建立图

小白BG.1 邻接矩阵表示的图结点的结构 typedef struct GNode *PtrToGNode;//PtrToGNode是指向GNode的一个指针 struct GNode{ int Nv;//顶点数 int Ne;//边数 WeightType G[MaxVertexNum][MaxVertexNum]; DataType Data[MaxVertexNum];//存顶点的数据 }; typedef PtrToGNode MG…...

计算机毕业设计Java大众采编本微资讯发布平台(源码+系统+mysql数据库+lw文档)

计算机毕业设计Java大众采编本微资讯发布平台(源码系统mysql数据库lw文档) 计算机毕业设计Java大众采编本微资讯发布平台(源码系统mysql数据库lw文档)本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xff1a;idea eclipse…...

web前端-javascript-立即执行函数(说明、例子)

立即执行函数 /* (function(){alert("我是一个匿名函数~~~"); })(); */(function (a, b) {console.log("a " a);console.log("b " b); })(123, 456);1. 说明 函数定义完&#xff0c;立即被调用&#xff0c;这种函数叫做立即执行函数立即执…...

【计算机视觉】图像形成与颜色

图像形成与颜色 光照及阴影 辐射度学 颜色 颜色信息反映了入射光的能量分布与波长&#xff0c;可见光的波长在400nm到760nm之间。 RGB RGB分别代表三个基色&#xff08;R-红色、G-绿色、B-蓝色&#xff09;&#xff0c;如(0,0,0)表示黑色、(255, 255, 255)表示白色。其中2…...

Musical Christmas Lights——一个圣诞树灯光✨随音乐节奏改变的前端开源项目

文章目录前言视频介绍项目截图项目地址项目源码以上就是本篇文章的全部内容&#xff0c;将你编写好的项目分享给你的朋友们或者那个TA吧&#xff01;制作不易&#xff0c;求个三连&#xff01;❤️ &#x1f4ac; ⭐️前言 今天博主在刷短视频时&#x1f610;&#xff0c;朋友推…...

[附源码]Python计算机毕业设计SSM进出口食品安全信息管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…...

电平触发的触发器

普通的SR锁存器没有任何抗干扰能力 我们要加控制信号&#xff0c;来抵抗干扰 比如说我们不把信号直接加在门上&#xff0c;我们可以再加一级门电路&#xff0c;让这个输出和输入不在同一个门上&#xff0c;我们希望加入一个控制信号&#xff0c;来控制电路工作的时刻 对电路结…...

php后端+JQuery+Ajax简单表单提交

通过ajax,如果从后端直接想前端返回数组,那前端收到的是一个‘Array’的字符串。所以,我比较习惯的是用json对象的格式。由后端通过json_encode()函数,把数组封装成对象,传递到前端;前端也以json的格式接收。这里用提交表单来举例说明。 页面显示如下: JQueryAjax.…...

论文投稿指南——中文核心期刊推荐(计算机技术2)

>>>深度学习Tricks&#xff0c;第一时间送达<<< 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊。下面&#xff0c;简单介绍下什么是中文核心期刊要目总览&#xff1a; 《中文核心期刊要目总…...

集合java

java集合 集合、数组都是对多个数据进行存储操作的结构&#xff0c;简称Java容器 此时的存储&#xff0c;主要是指内存层面的存储&#xff0c;不涉及持久化的存储&#xff08;txt,jpg,avi&#xff09; Java集合可分为Collection 和 Map 两种体系 1. Collection接口&#xff1…...

spring boot 应用mybatis

Mybatis入门: Mybatis入门_做测试的喵酱的博客-CSDN博客 目录 一、spring boot 应用mybatis 核心 二、举例&#xff1a; 2.1 背景 2.2 项目结构&#xff1a; 2.3 依赖包 pom 2.4 项目配置文件application.yml 2.5 实例层entity 2.6 mybatis的mapper层 2.7 spring boot…...

夜晚水闸3D可视化:科技魔法点亮水利新纪元

在宁静的夜晚&#xff0c;当城市的霓虹灯逐渐暗淡&#xff0c;你是否曾想过&#xff0c;那些默默守护着城市安全的水闸&#xff0c;在科技的魔力下&#xff0c;正焕发出别样的光彩&#xff1f;今天&#xff0c;就让我们一起走进夜晚水闸3D模型&#xff0c;感受科技为水利带来的…...

智慧楼宇方案简介

智慧楼宇总述 以人为本、以打造智慧楼宇为基础,以物联网服务公众创新为中心;多渠道、易办事、高效率、高节能、可复用、智慧化;采用适度超前、先进、适用、优化组合的成套技术体系,实现建立一个安全、舒适、通信便捷,环境优雅的数字化、网络化、智慧化的楼宇,满足对公众服…...

Spring Boot从入门到实战

课程介绍 本课程从SpringBoot的最基础的安装、配置开始到SpringBoot的日志管理、Web业务开发、数据存储、数据缓存&#xff0c;安全控制及相关企业级应用&#xff0c;全程案例贯穿&#xff0c;案例每一步的都会讲解实现思路&#xff0c;全程手敲代码实现。让你不仅能够掌Sprin…...

linux 内核扩展pcie 配置空间

某些pcie卡的配置空间很大&#xff0c;有些内核配置可能不支持较大的扩展空间的配置。这时通过设备文件读写pcie的地址空间驱动就会报Resource temporarily unavailable错误。通过lspci -d 卡id: -xxxx 可以查看pcie卡的配置空间。例如 但是这款卡的扩展配置空间很大&#xff0…...

C++队列queue用法详解

一、queue的介绍 1. 队列是一种容器适配器&#xff0c;专门用于在 FIFO 上下文 ( 先进先出 ) 中操作&#xff0c;其中从容器一端插入元素&#xff0c;另一端 提取元素。 2. 队列作为容器适配器实现&#xff0c;容器适配器即将特定容器类封装作为其底层容器类&#xff0c; qu…...

CUDA版本支持的pytorch版本

PyTorch 1.0.x - 支持 CUDA 7.5 PyTorch 1.1.x - 支持 CUDA 8.0 PyTorch 1.2.x - 支持 CUDA 9.0 PyTorch 1.3.x - 支持 CUDA 9.2 PyTorch 1.4.x - 支持 CUDA 10.1 PyTorch 1.5.x - 支持 CUDA 10.2 PyTorch 1.6.x - 支持 CUDA 11.0 PyTorch 1.7.x - 支持 CUDA 11.0/11.1 PyTorch…...