双语网站管理系统 div css/营销课程培训哪个机构好
会话跟踪技术(Cookie
&Session
)
-
注意: HTTP协议是无状态 的,即每次浏览器向服务器请求时,服务器都会将该请求视为新的请求,因此我们需要会话跟踪技术来实现会话内的数据共享
-
会话
- 当用户打开浏览器,访问Web服务器资源时会话建立,直到有一方断开连接时会话结束。在一次会话中可包含多次请求和响应
-
会话跟踪
- 它是一种维护浏览器状态的方法。服务器需要识别多次请求是否来自于同一个浏览器,以便可以在同一次会话的多次请求中共享数据
-
会话跟踪技术实现的方式
- 客户端会话跟踪技术:
Cookie
- 服务端会话跟踪技术:
Session
- 客户端会话跟踪技术:
Cookie
Cookie
基本使用
-
Cookie
:客户端会话跟踪技术,它会将数据保存到客户端,以后每次请求都携带Cookie数据进行访问 -
工作流程
- 假设有两个
Servlet
,为A和B。此时客户端在请求AServlet
数据时,AServlet
就会创建一个Cookie
对象(里面包含Cookie
数据),在AServlet
进行响应时就会把Cookie
发送给客户端,客户端会将其保存到客户端的内存中,当客户端在同一次会话中访问另一个BServlet
时会自动携带着Cookie
数据进行对BServlet
的访问
- 假设有两个
发送Cookie
用到的Cookie 构造器 | 解释 |
---|---|
public Cookie(String name, String value) | 创建Cookie 对象。name :表示Cookie的名称。这个名称用于标识Cookie,必须是唯一的,并且不能为空。value :表示Cookie的值。这个值是与Cookie名称相关联的数据。 |
用到的Response 的方法 | value :一个String 类型的参数,表示Cookie的值。这个值是与Cookie名称相关联的数据。解释 |
void addCookie(Cookie cookie) | 将一个 Cookie 对象添加到 HTTP 响应中。这使得服务器可以向客户端发送 Cookie,客户端浏览器会接收并保存这个 Cookie,并在后续请求中将其发送回服务器。 |
-
发送Cookie步骤
- Step1: 创建Cookie对象并设置数据
Cookie cookie = new Cookie("key","value");
- Step2: 使用Response对象发送
Cookie
到客户端response.addCookie(cookie);
- Step1: 创建Cookie对象并设置数据
-
代码示例
-
客户端访问A
Servlet
,AServlet
就会创建一个Cookie对象(里面包含Cookie数据),在AServlet
进行响应时就会把Cookie发送给客户端,客户端会将其保存到客户端的内存中package at.guigu.web.cookie;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@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", "zhangsan");//2 发送Cookieresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
Tomcat运行该Web项目后,截图如下
-
获取Cookie
用到的Request 接口中的方法 | 解释 |
---|---|
Cookie[] getCookies() | 获取客户端发送到服务器的所有Cookie。 |
用到的Cookie 对象的方法 | 解释 |
public String getName() | 返回Cookie的名称 |
public String getValue() | 返回Cookie 名对应的值 |
-
获取Cookie步骤
- Step1: 使用Request对象获取客户端携带的所有
Cookie
Cookie[] cookies = request.getCookies();
- Step2: 遍历数组获取每一个
Cookie
对象 - Step3: 使用
Cookie
对象方法获取数据:cookie.getName();
cookie.getValue();
- 注意:由于在实际项目中浏览器会保存很多
Cookie
信息,所以我们需要用到条件判断来筛选出我们需要的Cookie
信息
- 注意:由于在实际项目中浏览器会保存很多
- Step1: 使用Request对象获取客户端携带的所有
-
代码示例
客户端访问A
Servlet
后(此时Cookie在客户端内存中),访问另一个BServlet
,BServlet
获取客户端保存的Cookie
package at.guigu.web.cookie;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/bServlet") public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取Cookie//1 通过request对象获取Cookie数组Cookie[] cookies = request.getCookies();//2 遍历Cookie并获取Cookie的键值对for (Cookie cookie : cookies) {//3 通过Cookie对象获取AServlet发送的Cookie数据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);} }
Tomcat运行该Web项目后运行截图如下
Cookie
原理
-
Cookie的实现是基于HTTP协议的
- 响应头:
set-cookie
- 发送
Cookie
用到响应头
- 发送
- 请求头:
cookie
- 获取
Cookie
用到请求头
- 获取
- 响应头:
-
工作流程解释
- A
Servlet
发送Cookie
: AServlet
中创建了Cookie
数据,Tomcat会将该Cookie
对象响应给客户端浏览器,由于Tomcat是基于Http协议做出响应的,所以发送给客户端的是Http协议的响应数据,当Tomcat发现服务端要发送Cookie
给客户端浏览器时,会在响应数据中给Cookie加上一个响应头即set-cookie
,然后将其发送给客户端浏览器,客户端接收到该响应数据后通过响应头set-cookie
知道服务端发送的是Cookie
,则会对其进行解析,解析后则会拿到username=zhangsan
这个数据并将其存储到客户端的内存中 - B
Servlet
接收Cookie :当客户端携带Cookie数据来访问BServlet
时,浏览器会自动为Cookie
设置一个请求头cookie
,然后将其发送给BServlet
,然后BServlet
即可通过对应的方法来获取到客户端发送到服务器的所有Cookie
- A
-
代码示例
-
代码示例即为发送
Cookie
和获取Cookie
的代码示例,运行截图如下
-
Cookie
存活时间
Cookie 类中的方法 | 解释 |
---|---|
public void setMaxAge(int seconds) | 设置 Cookie 的最大存活时间。seconds 为秒数 |
-
默认情况下 Cookie存储在浏览器内存中,当浏览器被关闭时内存释放,则Cookie被销毁
- 即B
Servlet
能访问到AServlet
发送给浏览器的Cookie
是因为浏览器在发送请求给AServlet
后并没有关闭浏览器,如果浏览器在发送请求给AServlet
后关闭了浏览器,此时浏览器发送请求给BServlet
则BServlet
接收不到AServlet
发送给浏览器的Cookie
,除非AServlet
设置Cookie存活时间。
- 即B
-
可利用
setMaxAge(int seconds)
方法来设置Cookie存活时间- 正数: 将
Cooke
写入浏览器所在电脑的硬盘,持久化存储,但到时间会自动删除 - 负数: 默认值,
Cookie
在当前浏览器内存中,当浏览器关闭时Cookie会被立即销毁 - 零: 删除对应
Cookie
- 正数: 将
-
代码示例
-
A
Servlet
代码如下package at.guigu.web.cookie;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@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", "zhangsan");//设置Cookie存活时间--此处设置为2天cookie.setMaxAge(60*60*24*2);//2 发送Cookieresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
B
Servlet
代码详见获取Cookie
,Tomcat运行该Web项目后运行步骤截图如下所示-
Step1:访问A
Servlet
后关闭浏览器 -
Step2:重新打开一个浏览器,访问B
Servlet
,访问后服务端显示出Cookie
-
-
Cookie
存储中文
-
Cookie默认情况下不能存储中文,若存储则会报错
-
A
Servlet
的Cookie
存储中文方法:利用URL编码进行转码-
Step1:单独设置Cookie构造器的第二个参数value的值
-
Step2:将value进行转码:
value = URLEncoder.encode(value,"UTF-8");
-
Step3:创建Cookie对象:
Cookie cookie = new Cookie("key", value)
-
A
Servlet
代码示例如下package at.guigu.web.cookie;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.net.URLEncoder;@WebServlet("/aServlet") public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//发送Cookie//1 创建Cookie对象并设置数据String value = "张三";value = URLEncoder.encode(value, "UTF-8");Cookie cookie = new Cookie("username", value);//设置Cookie存活时间--此处设置为2天cookie.setMaxAge(60*60*24*2);//2 发送Cookieresponse.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
-
B
Servlet
获取Cookie
中的中文的方法:利用URL编码进行解码-
Step1:首先获取到对应的
value
值 -
Step2:将
value
值利用URLDecoder.decode(value, "UTF-8");
方法进行解码 -
B
Servlet
代码示例如下package at.guigu.web.cookie;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.net.URLDecoder; import java.net.URLEncoder;@WebServlet("/bServlet") public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取Cookie//1 通过request对象获取Cookie数组Cookie[] cookies = request.getCookies();//2 遍历Cookie并获取Cookie的键值对for (Cookie cookie : cookies) {//3 通过Cookie对象获取AServlet发送的Cookie数据String name = cookie.getName();if ("username".equals(name)) {String value = cookie.getValue();value = URLDecoder.decode(value, "UTF-8");System.out.println(name + ":" + value);break;}}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
Session
-
Session
:服务端会话跟踪技术,它会将数据保存到服务端 -
JavaEE提供了
HttpSession
接口,用来实现一次会话的多次请求间的数据共享功能 -
工作流程
- 假设有两个
Servlet
,为A和B。此时客户端在请求AServlet
数据时,AServlet
就会创建一个Cookie对象(里面包含Cookie
数据),在AServlet
进行响应时就会把Cookie
发送给客户端,客户端会将其保存到客户端的内存中,当客户端在同一次会话中访问另一个BServlet
时会自动携带着Cookie
数据进行对BServlet
的访问,这个过程就会造成Cookie
暴露在网络中,就会很不安全,为了解决该问题就有了Session
- A和B两个
Servlet
可以创建同一个Session
对象,我们可以通过AServlet
向Session
对象中存入数据;然后BServlet
也可以通过Session
对象来获取存入的数据 - 以上步骤即可实现一次会话中两个请求的数据共享
- 假设有两个
-
注意:一次会话的多次请求之间使用的是同一个
Session
对象
Session
基本使用
用到的Request接口的方法 | 解释 |
---|---|
HttpSession getSession(); | 获取当前会话,如果没有会话则创建一个新的会话Session 对象 |
用到的Session对象的方法 | 解释 |
void setAttribute(String name, Object o) | 存储数据到session域中 |
Object getAttribute(String name) | 根据key获取对应的值 |
void removeAttribute(String name) | 根据key删除对应的键值对 |
-
使用步骤
- Step1:通过
Request
对象来获取Session
对象即HttpSession session = request.getSession();
- Step2:利用Session对象相关方法执行对应的功能
- Step1:通过
-
代码示例
-
ASessionServlet
类代码package at.guigu.web.session;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/aSessionServlet") public class ASessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//向Session中存储数据//1 获取Session对象HttpSession session = request.getSession();//2 存储数据session.setAttribute("username", "zhangsan");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
BSessionServlet
类代码package at.guigu.web.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("/bSessionServlet") public class BSessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//从Session中获取数据//1 获取Session对象HttpSession session = request.getSession();//2 获取对应数据String username = (String) session.getAttribute("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
运行截图如下
-
Session
原理
-
Session
是基于Cookie
实现的 -
工作流程解释
- 浏览器在一次会话的第一次请求中,服务端
ASessionServlet
会首次创建一个Session
对象,并会自动会该对象分配一个唯一标识的ID
,在向Session
域中存储数据后,Tomcat会给浏览器做出响应,在做出响应的这个过程中会识别到Session
对象,当识别到Session
对象时会自动将该Session
对象对应的ID作为Cookie
传给客户端浏览器(即set-cookie:JSESSIONID=ID值
) - 当浏览器第二次请求时会将该
Cookie
(即cookie:JSESSIONID=ID值
)传给对应的服务端BSessionServlet
,当BSessionServlet
接收到该Cookie
后会自动根据ID值来找到对应的Session
对象 - 以上步骤即可保证在一次会话的多次请求间,服务器使用的是同一个
Session
对象
- 浏览器在一次会话的第一次请求中,服务端
-
代码示例
-
代码示例即为
Session
基本使用那一部分内容中的代码示例,访问服务端ASessionServlet
后再次访问服务端BSessionServlet
的运行截图分别如图一和图二所示
-
Session
钝化、活化
-
服务器 正常 关闭或 正常 重启后,
Session
中的数据依然存在,前提是浏览器处于一次会话- 钝化:在服务器正常关闭后,Tomcat会自动将
Session
数据写入到硬盘的文件中 - 活化:再次启动服务器后,Tomcat会自动将其重新加载到
Session
中
- 钝化:在服务器正常关闭后,Tomcat会自动将
-
若浏览器处于不同次会话,则重新访问服务器时,服务器会重新创建一次
Session
对象,而不是原来的Session
对象(即获得的Session
不会是同一个Session
),详见以下示例-
ASessionServlet
代码如下:package at.guigu.web.session;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/aSessionServlet") public class ASessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//向Session中存储数据//1 获取Session对象HttpSession session = request.getSession();//2 存储数据session.setAttribute("username", "zhangsan");System.out.println(session);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
运行截图如下,由图可知两次获得的Session对象不是同一个
-
-
浏览器多次请求可获得同一个
Session
的情景(即Session
共享的条件)- 浏览器处于同一次会话的多个请求
- 若浏览器完全关闭就不属于同一次会话
- 此时服务端若正常关闭或正常重启,只要浏览器处于同一次会话就仍可获得同一个
Session
对象
- 浏览器处于同一次会话的多个请求
Session
存活时间及销毁
-
默认情况下,若浏览器无任何操作则会在30分钟后自动销毁
Session
对象 -
更改自动销毁时间的方法
-
在web.xml文件中的
<web-app>
标签体内加入以下标签代码<session-config><session-timeout>自己要配置的时间,以分钟为单位</session-timeout> </session-config>
-
-
在Session自动销毁之前就销毁Session对象的方法
-
在浏览器请求的服务端(此处以第二次请求的服务端
BSessionServlet
为例)调用Session
对象的invalidate()
方法,代码如下所示- 销毁之后就无法获取Session中的任何数据了
package at.guigu.web.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("/bSessionServlet") public class BSessionServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//从Session中获取数据//1 获取Session对象HttpSession session = request.getSession();//销毁Session对象session.invalidate();//2 获取对应数据String username = (String) session.getAttribute("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
Cookie和Session的异同及应用场景
-
相同点
- 都是来完成一次会话内多次请求见的数据共享的
-
不同点
- 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大3KB,Session无大小限制
- 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
-
应用场景
-
购物车:使用Cookie来存储
-
记住我功能:使用Cookie来存储
- 比如记住用户名和密码,这些数据都是需要安全的,但是又由于Session只能短期存储,无法长期存储,所以只能用
Cookie
- 比如记住用户名和密码,这些数据都是需要安全的,但是又由于Session只能短期存储,无法长期存储,所以只能用
-
以登录用户的名称展示:使用Session来存储
-
验证码:使用session来存储
-
-
结论
- Cookie是用来保证用户在未登录情况下的身份识别
- Session是用来保存用户登录后的数据
会话跟踪技术登录案例
- 注意:该案例已上传到Gitee,可自行下载 :https://gitee.com/cgrs572/cookie-session-demo.git
需求说明
环境准备
-
创建新的Web项目BrandDemo,引入坐标(在pom.xml文件中引入坐标依赖),并配置Tomcat(可详见Tomcat部分)
-
需要的坐标依赖有mybatis、mysql驱动、servlet、jsp、jstl
-
需要的插件有Tomcat插件
-
完整pom.xml文件如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>BrandDemo</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>BrandDemo Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--MyBatis依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.16</version></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!--Servlet依赖--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope><!--依赖范围关键字provided:在编译环境和测试环境有效,但在真正运行时就不会在使用该jar包--></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><finalName>BrandDemo</finalName><plugins><!-- Tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins></build> </project>
-
-
创建三层架构包结构
-
创建数据库表
tb_user
并使IDEA与数据库建立连接,SQL代码如下DROP TABLE IF EXISTS tb_user; #创建表 CREATE TABLE tb_user (id INT PRIMARY KEY AUTO_INCREMENT,username varchar(20),password varchar(20),gender char(1)L,addr varchar(30), );#向表中添加数据 INSERT INTO `tb_user` VALUES (1, 'zhangsan', '123', '男', '北京'); INSERT INTO `tb_user` VALUES (2, '李四', '234', '女', '天津'); INSERT INTO `tb_user` VALUES (3, '王五', '11', '男', '西安');
-
创建实体类
User
(此为Pojo类 :存放对数据库中数据封装的对象),代码如下package at.guigu.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 + '\'' +'}';} }
-
MyBrtis基础环境配置
-
在mapper包下创建
UserMapper
接口 -
在项目的源代码配置文件目录(即main包下的resources目录下)创建多层目录,多层目录对应Mapper接口所在的多层包,然后再该包中创建
UserMapper.xml
SQL映射文件,基本代码如下<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace:名称空间 --> <mapper namespace="at.guigu.mapper.UserMapper"><!--由于数据库中的字段名与pojo包下User类中的属性名一致,所以不需结果映射--></mapper>
-
在项目的源代码配置文件目录(即main包下的resources目录下)创建MyBatis核心配置文件
mybatis-config.xml
,代码如下<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><!--设置别名--><typeAliases><package name="at.guigu.pojo"/></typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><!--数据库 连接信息--><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><!--加载SQL映射文件:传入sql映射文件的路径--> <!-- <mapper resource="at/guigu/mapper/BrandMapper.xml"/>--><package name="at.guigu.mapper"/></mappers> </configuration>
-
-
将JSP案例中的所有代码均移动到该Web项目下,使该登录案例登录成功后即为JSP案例中的页面,具体截图详见后续
用户登录
-
解释:若登录成功则进入用户个人主页,若登录失败则显示出对应错误之处
-
要完成该问则需满足三层架构,各层需要做的工作如下所示
-
Dao层/Map层
-
Step1:
UserMapper
接口中写入查询所有数据的方法package at.guigu.mapper;import at.guigu.pojo.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select;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); }
-
Step2: 在对应的SQL映射文件
UserMapper.xml
中写入SQL语句<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace:名称空间 --> <mapper namespace="at.guigu.mapper.UserMapper"><!--由于数据库中的字段名与pojo包下User类中的属性名一致,所以不需结果映射--><select id="select" resultType="user">select * from tb_userwhere username=#{username} and password=#{password};</select> </mapper>
-
-
Service层
-
由于Service层中的类均需要调用Mapper接口中的方法,那么该层中的类就都需要载核心配置文件,来获取
SqlSessionFactory
SQL连接工厂对象,所以就可以将SqlSessionFactory
封装为一个工具类。所以在完善Service
层之前需要先在util
包下将工具类SqlSessionFactoryUtils
创建好,代码如下:package at.guigu.util;import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException; import java.io.InputStream;public class SqlSessionFactoryUtils {private static SqlSessionFactory sqlSessionFactory;static {//静态代码快会随着类的加载而自动执行,且只执行一次try {//配置mybatis-config.xml文件路径。注意:若该文件直接在resources目录下,则直接写文件名即可String resource = "mybatis-config.xml";//利用Resources类中的静态方法将配置文件加载到内存InputStream inputStream = Resources.getResourceAsStream(resource);//获取SqlSessionFactory对象sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}public static SqlSessionFactory getSqlSessionFactory() {return sqlSessionFactory;} }
-
Step1: 在service包下创建
UserService
类来调用mapper包下的UserMapper接口中的select
方法,代码如下注意:获取
SqlSessionFactory
对象的代码放在了成员变量的位置,这样所有方法可共用该对象,并不需要重复获取package at.guigu.service;import at.guigu.mapper.UserMapper; import at.guigu.pojo.User; import at.guigu.util.SqlSessionFactoryUtils; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory;import java.util.List; public class UserService {//1 获取SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();/***登录方法* @param username* @param password* @return*/public User login(String username, String password) {//2 获取SqlSession对象,执行SQL语句//2.1 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//2.2 获取Mapper接口UserMapper的代理对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//2.3 执行sql语句User user = userMapper.select(username, password);//释放资源sqlSession.close();return user;} }
-
-
Web层
-
Step1:由于Tmcat运行该项目后,直接是用户登录页面,所以应该有个login.jsp登录页面代码,如下所示
- 注意:博主将无关修饰均已去除,只有个简单的静态登录页面
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Title</title></head><body><form action="/CookieSessionDemo/loginServlet" method="post"><h1>登录系统</h1>用户名:<input name="username" type="text"><br>密码:<input name="password" type="password"><br>记住账号:<input name="remember" type="checkbox"><br><input value="登录" type="submit"><!--这里提前写了一下注册的跳转链接,后面我们会把这个页面也写完--><a href="/CookieSessionDemo/register.jsp">没有账号?</a></form></body> </html>
其运行截图如下
-
Step2:创建
LoginServlet
类,且代码如下- 注意:创建一个私有的
UserService
对象应将其放在成员变量的位置,因为对于大工程来说可能会多次用到Service
对象 - 若登录成功,则跳转到查询所有品牌的页面,且该页面会欢迎登录的用户
- 若登陆失败,则重新返回到用户登录页面即login.jsp,且该页面给出提示信息
package at.guigu.web;import at.guigu.pojo.User; import at.guigu.service.BrandService; import at.guigu.service.UserService;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet {//1 创建一个私有的UserService对象private UserService userService = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//2 获取用户名和密码String username = request.getParameter("username");String password = request.getParameter("password");//3 调用Service查询用户名和密码是否正确User user = userService.login(username, password);if (user != null) {//用户登陆成功//1 将登陆成功后的User对象,存储到Session中HttpSession session = request.getSession();session.setAttribute("user", user);//2 跳转到查询所有页面//注意:由于登录界面和查询所有页面的请求没有资源共享,所以使用重定向//动态获取虚拟目录String contextPath = request.getContextPath();response.sendRedirect(contextPath + "/selectAllServlet");} else {//用户登录失败//将错误信息存储到request域中request.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);} }
-
若用户登录成功,则跳转到查询所有商品页面,且该页面会欢迎登录的用户,所以
brand.jsp
代码如下<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Title</title></head><body><h1>${user.username},欢迎您</h1><input type="button" value="新增" id="add"><br><hr><table border="1" cellspacing="0"><tr><th>序号</th><th>品牌名称</th><th>企业名称</th><th>排序</th><th>品牌介绍</th><th>状态</th><th>操作</th></tr><c:forEach items="${brands}" var="brand" varStatus="xuhao"><tr align="center"><td>${xuhao.count}</td><td>${brand.brandName}</td><td>${brand.companyName}</td><td>${brand.ordered}</td><td>${brand.description}</td><c:choose><c:when test="${brand.status==1}"><td>启用</td></c:when><c:otherwise><td>禁用</td></c:otherwise></c:choose><td><a href="/CookieSessionDemo/selectByIdServlet?id=${brand.id}">修改</a> <a href="#">删除</a></td></tr></c:forEach></table><script>document.getElementById("add").onclick = function () {//addBrand.jsp的路径location.href = "/CookieSessionDemo/addBrand.jsp";}</script></body> </html>
-
若登陆失败,则重新返回到用户登录页面即
login.jsp
,且该页面给出提示信息,所以login.jsp
更改后的代码如下<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Login</title></head><body><form action="/CookieSessionDemo/loginServlet" method="post"><h1>登录系统</h1><%--提示错误信息代码--%><div>${login_msg}</div>用户名:<input name="username" type="text"><br>密码:<input name="password" type="password"><br>记住账号:<input name="remember" type="checkbox"><br><input value="登录" type="submit"><!--这里提前写了一下注册的跳转链接,后面我们会把这个页面也写完--><a href="/CookieSessionDemo/register.jsp">没有账号?</a></form></body> </html>
- 注意:创建一个私有的
-
-
Tomcat运行该Web项目后截图如下所示
-
登录成功界面
-
登陆失败界面
-
记住用户——写Cookie
-
解释:若用户勾选
“记住账号”
,则下次访问登录页面时会 自动填充 用户名和密码 -
过程
- 将用户名和密码写入到Cookie中,并且进行持久化存储Cookie,下次访问浏览器时会自动携带Cookie
- 在页面获取到Cookie数据后,会将用户名和密码自动设置到用户名和密码框中
-
写Cookie的条件
- 用户登录成功
- 用户勾选
“记住账号”
复选框
-
需要对Web层进行更改,如下图所示
-
Web层
-
Step1:
login.jsp
代码如下- 在复选框中加入属性
value="1"
,这样只要复选框被勾选服务端就能通过获取到该复选框数据来判断是否用户“记住账号”
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Login</title></head><body><form action="/CookieSessionDemo/loginServlet" method="post"><h1>登录系统</h1><%--提示错误信息--%><div>${login_msg}</div>用户名:<input name="username" type="text"><br>密码:<input name="password" type="password"><br>记住账号:<input name="remember" type="checkbox" value="1"><br><input value="登录" type="submit"><!--这里提前写了一下注册的跳转链接,后面我们会把这个页面也写完--><a href="/CookieSessionDemo/register.jsp">没有账号?</a></form></body> </html>
- 在复选框中加入属性
-
Step2:
LoginServlet
类代码如下package at.guigu.web;import at.guigu.pojo.User; import at.guigu.service.BrandService; import at.guigu.service.UserService;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet {//1 创建一个私有的UserService对象private UserService userService = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//2 获取用户名和密码以及获取复选框数据String username = request.getParameter("username");String password = request.getParameter("password");// 获取复选框数据String remember = request.getParameter("remember");//3 调用Service查询用户名和密码是否正确User user = userService.login(username, password);if (user != null) {//用户登陆成功//1 判断用户是否勾选复选框(即是否勾选记住账号)if ("1".equals(remember)) {//1.1 创建Cookie对象Cookie c_username = new Cookie("username", username);Cookie c_password = new Cookie("password", password);//1.2 设置Cookie存活时间——均设置为7天c_username.setMaxAge(60 * 60 * 24 * 7);c_password.setMaxAge(60 * 60 * 24 * 7);//1.3 将其发送到客户端response.addCookie(c_username);response.addCookie(c_password);}//2 将登陆成功后的User对象,存储到Session中HttpSession session = request.getSession();session.setAttribute("user", user);//3 跳转到查询所有页面//注意:由于登录界面和查询所有页面的请求没有资源共享,所以使用重定向//动态获取虚拟目录String contextPath = request.getContextPath();response.sendRedirect(contextPath + "/selectAllServlet");} else {//用户登录失败//将错误信息存储到request域中request.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);} }
-
-
Tomcat运行成功后截图如下
-
Step1:输入用户名和密码并勾选
“记住账号”
后单击登录 -
Step2:登录成功,会进入到查询所有商品的页面。由于是通过
LoginServlet
类向客户端服务器发送的响应,所以需要在开发者工具中找到loginServlet
,打开后如图所示
-
记住用户——获取Cookie
-
解释:勾选
“记住账号”
后在下一次打开浏览器登录时会自动填充用户名和密码- 用户名和密码的获取是在登录界面
login.jsp
的代码中直接获取 (此处用到EL表达式) 并填写到对应的用户名和密码框中
- 用户名和密码的获取是在登录界面
-
EL表达式获取到Cookie的代码方式为
EL代码 解释 ${cookie.key.value}
通过键key获取到对应的键值。 key
指存储在Cookie
中自己所设置的键的名称 -
login.jsp代码如下
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Login</title></head><body><form action="/CookieSessionDemo/loginServlet" method="post"><h1>登录系统</h1><%--提示错误信息--%><div>${login_msg}</div>用户名:<input name="username" type="text" value="${cookie.username.value}"><br>密码:<input name="password" type="password" value="${cookie.password.value}"><br>记住账号:<input name="remember" type="checkbox" value="1"><br><input value="登录" type="submit"><!--这里提前写了一下注册的跳转链接,后面我们会把这个页面也写完--><a href="/CookieSessionDemo/register.jsp">没有账号?</a></form></body> </html>
-
Tomcat运行该Web项目后,只要在输入用户名和密码之后勾选
“记住账号”
,则在下一次打开浏览器登录时会自动填充用户名和密码,如图所示
用户注册
-
解释:将用户信息保存到数据库,使用户下次能够登录
-
注册涉及的功能
- 注册功能:保存用户信息至数据库
- 验证码功能:
- 展示验证码:展示验证码图片,并且可以点击切换
- 校验验证码:验证码填写不正确,则注册失败
-
要完成该问则需满足三层架构,各层需要做的工作如下所示
-
Dao层/Map层
-
Step1:
UserMapper
接口中写入对应的方法package at.guigu.mapper;import at.guigu.pojo.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select;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*///@Select("insert into tb_user(username, password) values (#{username}, #{password})")void add(User user); }
-
Step2:在对应的SQL映射文件
UserMapper.xml
中写入SQL语句<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace:名称空间 --> <mapper namespace="at.guigu.mapper.UserMapper"><!--由于数据库中的字段名与pojo包下User类中的属性名一致,所以不需结果映射--><select id="select" resultType="user">select * from tb_userwhere username=#{username} and password=#{password};</select><select id="selectByUsername" resultType="at.guigu.pojo.User">select * from tb_user where username=#{username};</select><insert id="add">insert into tb_user(username, password) values (#{username}, #{password});</insert> </mapper>
-
-
Service层
-
工具类
SqlSessionFactoryUtils
代码省略,详见用户登录中的SqlSessionFactoryUtils
代码 -
Step1: 在service包下创建
UserService
类来调用mapper包下的UserMapper
接口中的方法,代码如下package at.guigu.service;import at.guigu.mapper.UserMapper; import at.guigu.pojo.User; import at.guigu.util.SqlSessionFactoryUtils; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory;import java.util.List; public class UserService {//1 获取SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();/***登录方法* @param username* @param password* @return*/public User login(String username, String password) {//2 获取SqlSession对象,执行SQL语句//2.1 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//2.2 获取Mapper接口UserMapper的代理对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//2.3 执行sql语句User user = userMapper.select(username, password);//释放资源sqlSession.close();return user;}/*** 注册功能* @param user* @return*/public boolean register(User user) {//2 获取SqlSession对象,执行SQL语句//2.1 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//2.2 获取Mapper接口UserMapper的代理对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//3 判断用户名是否存在,若不存在则可添加用户User u = userMapper.selectByUsername(user.getUsername());if (u == null) {//用户名不存在,注册//4 执行sql语句userMapper.add(user);//5 注意:增删改的SQL语句需要手动提交事务让其生效sqlSession.commit();}//释放资源sqlSession.close();//当u!=null时代表用户名存在,会返回false;反之会返回truereturn u==null;} }
-
-
Web层
-
Step1:在Web项目核心目录下创建
register.jsp
,且代码如下<%--Created by IntelliJ IDEA.User: 10195Date: 2024/7/2Time: 13:54To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Title</title></head><body><form action="/CookieSessionDemo/registerServlet" method="post"><h1>欢迎注册</h1><!--已有账号的话就跳转至登录页面-->已有账号?<a href="login.jsp">点击登录</a><br><%--提示错误信息--%><div>${register_msg}</div>用户名:<input name="username" type="text"><br>密码:<input name="password" type="password"><br><input value="注册" type="submit"></form></body> </html>
-
Step2:创建
RegisterServlet
类且代码如下package at.guigu.web;import at.guigu.pojo.User; import at.guigu.service.UserService;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/registerServlet") public class RegisterServlet extends HttpServlet {//1 创建一个私有的UserService对象private UserService userService = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//2 接收用户信息:获取用户名和密码以及获取复选框数据String username = request.getParameter("username");String password = request.getParameter("password");User user = new User();user.setUsername(username);user.setPassword(password);//3 调用Service注册用户Boolean flag = userService.register(user);//4 判断是否注册成功if (flag) {//注册成功request.setAttribute("register_msg", "注册成功,请登录");//跳回到用户登录页面即login.jsprequest.getRequestDispatcher("/login.jsp").forward(request, response);} else {//注册失败//将错误信息存储到request域中request.setAttribute("register_msg", "用户名重复");//跳回到用户登录页面即login.jsprequest.getRequestDispatcher("/register.jsp").forward(request, response);}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
-
Tomcat运行该Web项目后运行截图如下所示
-
单击
“没有账号”
跳转到注册页面 -
注册失败:注册时若用户名已存在则会提示
-
注册城成功后会自动跳转到登录界面并提示注册成功
-
验证码展示
-
解释:展示验证码图片并可以通过单击切换验证码
-
验证码原理:它其实就是Java代码生成的图片
-
验证码作用:防止机器自动注册以此来攻击服务器
-
实现流程
- 前端发送请求给
CheckCodeServlet
CheckCodeServlet
接收到请求后,生成验证码图片,将图片用Reponse
对象的输出流写回到前端
- 前端发送请求给
-
Service层
-
验证码工具类
CheckCodeUtil
代码如下package at.guigu.util;import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.Random;/*** 生成验证码工具类*/ public class CheckCodeUtil {//验证码可取的字符public static final String VERIFY_CODES = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";private static Random random = new Random();/*** 输出随机验证码图片流,并返回验证码值(一般传入输出流,响应response页面端,Web项目用的较多)** @param width 验证码图片的宽度* @param height 验证码图片的高度* @param os 输出流* @param verifySize 数据长度:即规定验证码的位数* @return 返回验证码数据* @throws IOException*/public static String outputVerifyImage(int width, int height, OutputStream os, int verifySize) throws IOException {//生成验证码字符串数据String verifyCode = generateVerifyCode(verifySize);//生成验证码图片outputImage(width, height, os, verifyCode);//返回验证码字符串数据return verifyCode;}/*** 使用系统默认字符源生成验证码** @param verifySize 验证码长度* @return*/public static String generateVerifyCode(int verifySize) {return generateVerifyCode(verifySize, VERIFY_CODES);}/*** 使用指定源生成验证码** @param verifySize 验证码长度* @param sources 验证码字符源* @return*/public static String generateVerifyCode(int verifySize, String sources) {// 未设定展示源的字码,赋默认值大写字母+数字if (sources == null || sources.length() == 0) {sources = VERIFY_CODES;}int codesLen = sources.length();Random rand = new Random(System.currentTimeMillis());StringBuilder verifyCode = new StringBuilder(verifySize);for (int i = 0; i < verifySize; i++) {verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));}return verifyCode.toString();}/*** 生成随机验证码文件,并返回验证码值 (生成图片形式,用的较少)** @param width* @param height* @param outputFile* @param verifySize* @return* @throws IOException*/public static String outputVerifyImage(int width, int height, File outputFile, int verifySize) throws IOException {String verifyCode = generateVerifyCode(verifySize);outputImage(width, height, outputFile, verifyCode);return verifyCode;}/*** 生成指定验证码图像文件** @param width* @param height* @param outputFile* @param code* @throws IOException*/public static void outputImage(int width, int height, File outputFile, String code) throws IOException {if (outputFile == null) {return;}File dir = outputFile.getParentFile();//文件不存在if (!dir.exists()) {//创建dir.mkdirs();}try {outputFile.createNewFile();FileOutputStream fos = new FileOutputStream(outputFile);outputImage(width, height, fos, code);fos.close();} catch (IOException e) {throw e;}}/*** 输出指定验证码图片流** @param w* @param h* @param os* @param code* @throws IOException*/public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {int verifySize = code.length();BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);Random rand = new Random();Graphics2D g2 = image.createGraphics();g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 创建颜色集合,使用java.awt包下的类Color[] colors = new Color[5];Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN,Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,Color.PINK, Color.YELLOW};float[] fractions = new float[colors.length];for (int i = 0; i < colors.length; i++) {colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];fractions[i] = rand.nextFloat();}Arrays.sort(fractions);// 设置边框色g2.setColor(Color.GRAY);g2.fillRect(0, 0, w, h);Color c = getRandColor(200, 250);// 设置背景色g2.setColor(c);g2.fillRect(0, 2, w, h - 4);// 绘制干扰线Random random = new Random();// 设置线条的颜色g2.setColor(getRandColor(160, 200));for (int i = 0; i < 20; i++) {int x = random.nextInt(w - 1);int y = random.nextInt(h - 1);int xl = random.nextInt(6) + 1;int yl = random.nextInt(12) + 1;g2.drawLine(x, y, x + xl + 40, y + yl + 20);}// 添加噪点// 噪声率float yawpRate = 0.05f;int area = (int) (yawpRate * w * h);for (int i = 0; i < area; i++) {int x = random.nextInt(w);int y = random.nextInt(h);// 获取随机颜色int rgb = getRandomIntColor();image.setRGB(x, y, rgb);}// 添加图片扭曲shear(g2, w, h, c);g2.setColor(getRandColor(100, 160));int fontSize = h - 4;Font font = new Font("Algerian", Font.ITALIC, fontSize);g2.setFont(font);char[] chars = code.toCharArray();for (int i = 0; i < verifySize; i++) {AffineTransform affine = new AffineTransform();affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2);g2.setTransform(affine);g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);}g2.dispose();ImageIO.write(image, "jpg", os);}/*** 随机颜色** @param fc* @param bc* @return*/private static Color getRandColor(int fc, int bc) {if (fc > 255) {fc = 255;}if (bc > 255) {bc = 255;}int r = fc + random.nextInt(bc - fc);int g = fc + random.nextInt(bc - fc);int b = fc + random.nextInt(bc - fc);return new Color(r, g, b);}private static int getRandomIntColor() {int[] rgb = getRandomRgb();int color = 0;for (int c : rgb) {color = color << 8;color = color | c;}return color;}private static int[] getRandomRgb() {int[] rgb = new int[3];for (int i = 0; i < 3; i++) {rgb[i] = random.nextInt(255);}return rgb;}private static void shear(Graphics g, int w1, int h1, Color color) {shearX(g, w1, h1, color);shearY(g, w1, h1, color);}private static void shearX(Graphics g, int w1, int h1, Color color) {int period = random.nextInt(2);boolean borderGap = true;int frames = 1;int phase = random.nextInt(2);for (int i = 0; i < h1; i++) {double d = (double) (period >> 1)* Math.sin((double) i / (double) period+ (6.2831853071795862D * (double) phase)/ (double) frames);g.copyArea(0, i, w1, 1, (int) d, 0);if (borderGap) {g.setColor(color);g.drawLine((int) d, i, 0, i);g.drawLine((int) d + w1, i, w1, i);}}}private static void shearY(Graphics g, int w1, int h1, Color color) {int period = random.nextInt(40) + 10; // 50;boolean borderGap = true;int frames = 20;int phase = 7;for (int i = 0; i < w1; i++) {double d = (double) (period >> 1)* Math.sin((double) i / (double) period+ (6.2831853071795862D * (double) phase)/ (double) frames);g.copyArea(i, 0, 1, h1, 0, (int) d);if (borderGap) {g.setColor(color);g.drawLine(i, (int) d, i, 0);g.drawLine(i, (int) d + h1, i, h1);}}} }
-
-
Web层
-
Step1:
register.jsp
文件代码更改如下:- 注意:
- 验证码图片路径为
CheckCodeServlet
- 给图片绑定一个单击事件,只要单击图片就会重新生成验证码
- 给
看不清?
绑定一个单击事件,只要单击看不清?
就会重新生成验证码- 给
看不清?
绑定的单击事件对应的图片路径要加上一个时间戳,原因是:在单击看不清?
之前所生成的验证码数据图片已经被浏览器缓存,所以若不加时间戳则单击看不清?
后验证码数据不会改变,使用的是被缓存的验证码数据图片
- 给
- 验证码图片路径为
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html><head><title>Title</title></head><body><form action="/CookieSessionDemo/registerServlet" method="post"><h1>欢迎注册</h1><!--已有账号的话就跳转至登录页面-->已有账号?<a href="login.jsp">点击登录</a><br><%--提示错误信息--%><div>${register_msg}</div>用户名:<input name="username" type="text"><br>密码:<input name="password" type="password"><br>验证码:<input name="checkCode" type="text"><img id="checkCodeImg" src="/CookieSessionDemo/checkCodeServlet"><a href="#" id="checkImg">看不清?</a><input value="注册" type="submit"></form><script>document.getElementById("checkCodeImg").onclick = function () {//路径后面加一个时间戳,能保证生成的图片永远不一样,避免浏览器缓存静态资源document.getElementById("checkCodeImg").src = "/CookieSessionDemo/checkCodeServlet?" + new Date().getMilliseconds();}document.getElementById("checkImg").onclick = function () {//路径后面加一个时间戳,能保证生成的图片永远不一样,避免浏览器缓存静态资源document.getElementById("checkCodeImg").src = "/CookieSessionDemo/checkCodeServlet?" + new Date().getMilliseconds();}</script></body> </html>
- 注意:
-
Step2:创建
CheckCodeServlet
类且代码如下package at.guigu.web;import at.guigu.util.CheckCodeUtil;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.io.OutputStream;@WebServlet("/checkCodeServlet") public class CheckCodeServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取字节输出流ServletOutputStream sos = response.getOutputStream();//获取验证码字符串数据String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, sos, 4);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
-
Tomcat运行该Web项目,运行截图如下
验证码校验
-
解释
- 判断程序生成的验证码和用户输入的验证码是否一样,若不一样则阻止注册
- 验证码图片和提交注册表单是两次请求,所以要将程序生成的验证码存入
Session
中
-
Web层工作流程如下
-
Web层
-
Step1:
CheckCodeServlet
文件代码更改如下:package at.guigu.web;import at.guigu.util.CheckCodeUtil;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.io.OutputStream;@WebServlet("/checkCodeServlet") public class CheckCodeServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1 生成验证码//获取字节输出流ServletOutputStream sos = response.getOutputStream();//调用工具类获取验证码字符串数据并生成验证码数据图片String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, sos, 4);//2 将系统自动生成的验证码字符串存入SessionHttpSession session = request.getSession();session.setAttribute("checkCodeGen", checkCode);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
Step2:
RegisterServlet
文件代码更改如下:package at.guigu.web;import at.guigu.pojo.User; import at.guigu.service.UserService;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException;@WebServlet("/registerServlet") public class RegisterServlet extends HttpServlet {//1 创建一个私有的UserService对象private UserService userService = new UserService();@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//2 接收用户信息:获取用户名和密码以及获取复选框数据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");//获取程序自动生成的验证码HttpSession session = request.getSession();String checkCodeGen = (String) session.getAttribute("checkCodeGen");//验证验证码是否正确:此处忽略大小写if (!checkCodeGen.equalsIgnoreCase(checkCode)) {//如果不一样则不允许注册,即注册失败//将错误信息存储到request域中request.setAttribute("register_msg", "验证码错误,请重新输入");//跳回到用户登录页面即login.jsprequest.getRequestDispatcher("/register.jsp").forward(request, response);return;//结束执行本java文件中的后续代码}//3 调用Service注册用户Boolean flag = userService.register(user);//4 判断是否注册成功if (flag) {//注册成功request.setAttribute("login_msg", "注册成功,请登录");//跳回到用户登录页面即login.jsprequest.getRequestDispatcher("/login.jsp").forward(request, response);} else {//注册失败//将错误信息存储到request域中request.setAttribute("register_msg", "用户名重复");//跳回到用户登录页面即login.jsprequest.getRequestDispatcher("/register.jsp").forward(request, response);}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);} }
-
-
Tomcat运行该Web项目,运行截图如下
-
验证码错误
-
验证码正确
-
相关文章:

大数据------JavaWeb------会话跟踪技术(Cookie、Session)(完整知识点汇总)
会话跟踪技术(Cookie&Session) 注意: HTTP协议是无状态 的,即每次浏览器向服务器请求时,服务器都会将该请求视为新的请求,因此我们需要会话跟踪技术来实现会话内的数据共享 会话 当用户打开浏览器&am…...

crossJoin笛卡尔积
crossJoin笛卡尔积 在Spark中,crossJoin方法用于执行两个数据集之间的笛卡尔积操作。具体来说,如果有两个数据集(DataFrame或Dataset),调用crossJoin方法将会生成一个新的数据集,其中包含两个原始数据集中所…...

Java客户端调用SOAP方式的WebService服务实现方式分析
简介 在多系统交互中,有时候需要以Java作为客户端来调用SOAP方式的WebService服务,本文通过分析不同的调用方式,以Demo的形式,帮助读者在生产实践中选择合适的调用方式。 本文JDK环境为JDK17。 结论 推荐使用Axis2或者Jaxws&#…...

华为机试真题--字符串序列判定
题目描述: 输入两个字符串S和L,都只包含英文小写字母,其中S长度<=100,L长度<=500000,请判定S是否是L的有效字串。 判定规则: S中的每个字符在L中都能找到(可以不连续),且S在L中字符的前后顺序与S中顺序要保持一致。(例如,S="ace"是L="abcd…...

Linux内核 -- 虚拟化之virtqueue结构
Linux Kernel中的Virtqueue Virtqueue是Linux Kernel中用于实现Virtio设备的一个关键数据结构。Virtio是一种虚拟I/O设备标准,旨在简化虚拟化环境中虚拟设备与虚拟机之间的通信。Virtqueue则是实现这种通信的核心机制。以下是Virtqueue的一些关键点: V…...

【pytorch18】Logistic Regression
回忆线性回归 for continuous:y xwbfor probability output:yσ(xwb) σ:sigmoid or logistic 线性回归是简单的线性模型,输入是x,网络参数是w和b,输出是连续的y的值 如何把它转化为分类问题?加了sigmoid函数,输出的值不再是…...

PostgreSQL的使用
PostgreSQL的使用 1.首先,使用docker进行安装pgvector数据库,具体的安装步骤可以查看我之前发的博文。 2.docker exec -it pgvector /bin/bash 进入docker容器内部,操作数据库,上述命令是以交互式命令进入了容器的内部…...

python 高级技巧 0706
python 33个高级用法技巧 列表推导式 简化了基于现有列表创建新列表的过程。 squares [x**2 for x in range(10)] print(squares)[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]字典推导式 用简洁的方式创建字典。 square_dict {x: x**2 for x in range(10)} print(square_dict){0…...

面试经典 106. 从中序与后序遍历序列构造二叉树
最近小胖开始找工作了,又来刷苦逼的算法了 555 废话不多说,看这一题,上链接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/?envTypestudy-plan-v2&envIdtop-inte…...

如何解决群晖Docker注册表查询失败/无法拉取镜像等问题
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 问题概述 📒📒 解决方案 📒🔖 方法一🔖 方法二🔖 方法三⚓️ 相关链接 🚓️📖 介绍 📖 在群晖(Synology)NAS设备上使用Docker时,我们可能会遇到查询Docker注册表失败,无法拉取Docker镜像的问题。这种情况…...

【Scrapy】 深入了解 Scrapy 中间件中的 process_spider_input 方法
准我快乐地重饰演某段美丽故事主人 饰演你旧年共寻梦的恋人 再去做没流着情泪的伊人 假装再有从前演过的戏份 重饰演某段美丽故事主人 饰演你旧年共寻梦的恋人 你纵是未明白仍夜深一人 穿起你那无言毛衣当跟你接近 🎵 陈慧娴《傻女》 Scrapy 是…...

数据库MySQL---基础篇
存储和管理数据的仓库 MySQL概述 数据库相关概念 数据库(DataBase)---数据存储的仓库,数据是有组织的进行存储 数据库管理系统(DBMS)-----操纵和管理数据库的大型软件 SQL----操作关系型数据库的编程语言ÿ…...

欧姆龙安全PLC及周边产品要点指南
电气安全、自动化设备作业安全,向来是非常非常之重要的!越来越多的客户在规划新产线、改造既有产线的过程中,明确要求设计方和施工方将安全考虑进整体方案中进行考虑和报价!作为一名自动化电气工程师,尤其是高级工程师…...

tableau气泡图与词云图绘制 - 8
气泡图及词云图绘制 1. 气泡图绘制1.1 选择相关属性字段1.2 选择气泡图1.3 设置颜色1.4 设置标签1.5 设置单位 2. 气泡图绘制 - 22.1 类别筛选2.2 页面年份获取2.3 行列获取2.4 历史轨迹显示 3. 词云图绘制3.1 筛选器3.2 选择相关属性3.3 选择气泡图3.4 设置类型颜色3.5 设置形…...

C语言 找出一个二维数组中的鞍点
找出一个二维数组中的鞍点,即该位置上的元素在该行上最大、在该列上最小。也可能没有鞍点。 #include <stdio.h>int main() {int matrix[4][4] {{10, 17, 13, 28},{21, 14, 16, 40},{30, 42, 23, 39},{24, 11, 19, 17}};int n 4, m 4;int found 0;for (int i 0; i …...

【笔记】在linux中设置错文件如何重置
以mysql的auto.cnf文件为例...

DNS中的CNAME与A记录:为什么无法共存A解析和C解析?
在互联网的世界中,DNS(域名系统)扮演着至关重要的角色,它将易于记忆的域名转换为计算机可识别的IP地址。在这个过程中,两种常见的DNS记录类型——CNAME记录和A记录——经常被提及。然而,它们之间存在着一些…...

线程和进程
文章目录 进程和线程进程线程案例 时间片概念调度方式线程的创建和启动第一种创建方式第二种创建方式(匿名内部类)第三种创建方式(Runnable接口)main线程和t线程之间的关系 线程的名字线程的优先级线程状态 进程和线程 进程 在计…...

【JavaEE】 简单认识CPU
🐵本篇文章将对cpu的相关知识进行讲解 一、认识CPU 下图是简略的冯诺依曼体系结构图 上图中,存储器用来存储数据,注意在存储器中都是以二进制的形式存储数据的,CPU就是中央处理器,其功能主要是进行各种算术运算和各种…...

《数字图像处理-OpenCV/Python》第17章:图像的特征描述
《数字图像处理-OpenCV/Python》第17章:图像的特征描述 本书京东 优惠购书链接 https://item.jd.com/14098452.html 本书CSDN 独家连载专栏 https://blog.csdn.net/youcans/category_12418787.html 第17章:图像的特征描述 特征检测与匹配是计算机视觉的…...

考研数学什么时候开始强化?如何保证进度不掉队?
晚了。我是实在人,不给你胡乱吹,虽然晚了,但相信我,还有的救。 实话实说,从七月中旬考研数一复习完真的有点悬,需要超级高效快速... 数二的时间也有点紧张... 中间基本没有试错的时间,让你换…...

Node.js的下载、安装和配置
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...

java.util.Properties类介绍
java.util.Properties 是 Java 编程语言中的一个类,用于管理应用程序的配置信息,它继承自 java.util.Hashtable 类,因此它也是基于键值对的数据结构。主要用途是存储应用程序的配置参数,比如数据库连接信息、用户设置等。 以下是 Properties 类的一些主要特点和用法: 存储…...

SpringBoot后端验证码-防止密码爆破功能
一、简介 为了防止网站的用户被通过密码典爆破。引入验证码的功能是十分有必要的。而前端的验证码又仅仅是只防君子不防小人。通过burpsuit等工具很容易就会被绕过。所以后端实现的验证码才是对用户信息安全的一大重要保障。 实现思路: 1.引入图形生成的依赖 2.生成…...

ChatEval:通过多代理辩论提升LLM文本评估质量
论文地址:ChatEval: Towards Better LLM-based Evaluators through Multi-Agent Debate | OpenReviewText evaluation has historically posed significant challenges, often demanding substantial labor and time cost. With the emergence of large language models (LLMs…...

关于美国服务器IP的几个常见问题
在租用美国服务器时,与之密切相关的一个要素就是IP,关于IP的问题总是有人问起,这里列举几项常见的问题,以供参考。 一、IP收费吗? 一般情况下,在租用服务器时,会赠送几个IP,因为这些…...

redis运维:sentinel模式如何查看所有从节点
1. 连接到sentinel redis-cli -h sentinel_host -p sentinel_port如: redis-cli -h {域名} -p 200182. 发现Redis主服务器 连接到哨兵后,我们可以使用SENTINEL get-master-addr-by-name命令来获取当前的Redis主服务器的地址。 SENTINEL get-master-a…...

价格疑云?格行WiFi创始人亲解谜团,性价比之王如何炼成?
随身wifi行业乱象频出,作为行业领跑品牌的格行随身wifi,关于价格问题一直备受质疑。关于设备上的“格行自有格行的骄傲”也被外界认定为是自大,甚至发展的线下一万多家门店也被同行不认可。近日,企业财经专访记者有幸采访了格行随…...

揭秘“消费即赚”的循环购模式
大家好,我是吴军,今天我将带您深入探索一种颠覆传统的新型商业模式——循环购模式。在这个模式中,消费者不仅能享受到购物的乐趣,还能通过消费获得实实在在的回报,甚至实现“边消费边赚钱”的奇妙体验。您是否好奇&…...

javaweb个人主页设计(html+css+js)
目录 1 前言和要求 1.1 前言 1.2 设计要求 2 预览 2.1 主页页面 2.2 个人简介 2.3 个人爱好 2.4 个人成绩有代码,但是图片已省略,可以根据自己情况添加 2.5 收藏夹 3 代码实现 3.1 主页 3.2 个人简介 3.3 个人爱好 3.4 个人成绩ÿ…...