2-10-3Cookie&Session
1.会话跟踪技术概述
- 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
- HTTP协议是无状态的,每次浏览器向服务器请求时,服务器都会将该请求视为新的请求,因此我们需要会话跟踪技术来实现会话内数据共享
2.Cookie基本使用
- Cookie:客户端会话技术,将数据保存到客户端,以后每次请求都携带Cookie数据进行访问
- Cookie基本使用
发送Cookie
1.创建Cookie对象,设置数据
Cookie cookie = new Cookie("key","value");
2.发送Cookie到客户端:使用response对象
response.add Cookie(cookie);
获取Cookie
3.获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
4.遍历数组,获取每一个Cookie对象:for
5.使用Cookie对象方法获取数据
cookie.getName();
cookie.getValue();
3.Cookie原理
Cookie的实现是基于HTTP协议的
- 响应头:set-cookie
- 请求头:cookie
工作原理步骤
- 服务器发 Cookie:用户访问网页,服务器处理请求时,在 HTTP 响应头用
Set - Cookie
字段发 Cookie 给浏览器。比如服务器想记住用户身份,就发个含身份信息的 Cookie。 - 浏览器存 Cookie:浏览器收到响应,解析
Set - Cookie
字段,把 Cookie 存本地,有内存和硬盘两种存储方式。 - 浏览器传 Cookie:浏览器再次访问同一服务器时,会在 HTTP 请求头的
Cookie
字段带上之前存的 Cookie 信息。 - 服务器用 Cookie:服务器收到请求,解析
Cookie
字段获取信息,根据信息做相应处理,像识别用户身份。
Cookie 属性作用
- Name - Value:存具体数据。
- Domain 和 Path:限定能访问 Cookie 的域名和页面路径。
- Expires/Max - Age:设定 Cookie 过期时间,没设就是会话 Cookie,关浏览器就删。
- Secure:让 Cookie 只在 HTTPS 请求中传输,更安全。
- HttpOnly:使 Cookie 只能通过 HTTP 请求访问,防脚本攻击偷信息。
4.Cookie使用细节
Cookie存活时间
默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁
setMaxAge(int seconds):设置Cookie存活时间
1.正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。至到时间自动删除
2.负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁
3.零:删除对应Cookie
//发送Cookie
//1. 创建Cookie对象
Cookie cookie = new Cookie("username", "zs");
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7);
//2. 发送Cookie,response
response.addCookie(cookie);
}
5.Cookie使用细节-存储中文
- Cookie存储中文
- Cookie不能直接存储中文
- 如需要存储,则需要进行转码:URL编码
//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解码
value = URLDecoder.decode(value, "UTF-8");
System.out.println(name+":"+value);
}
}
6.Session基本使用
- 服务端会话跟踪技术:将数据保存到服务端
- JavaEE提供HttpSession接口,来实现一次会话的多次请求间数据共享功能
- 使用:
1.获取Session对象
Http Session session = request.getSession();
2.Session对象功能:
- void setAttribute(String name, Objecto):存储数据到session 成中
- Object getAttribute(String name):根据key,获取值
- void removeAttribute(String name):根据key,删除该键值双时
7.Session原理
//存储到Session中
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 存储数据
session.setAttribute("username", "zs");
//获取数据,从session中
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 获取数据
Object username = session.getAttribute("username");
System.out.println(username);
}
8.Session-使用细节
- Session钝化、活化:
- 服务器重启后,Session中的数据是否还在?
- 钝化:在服务器正常关闭后,Tomcat会自动将Session数据写入硬盘的文件中
- 活化:再次启动服务器后,从文件中加载数据到Session中
- 服务器重启后,Session中的数据是否还在?
- Seesion销毁:
- 默认情况下,无操作,30分钟自动销毁
<session-config>
<session-timeout>30</session-timeout>
</session-config>
- 调用Session对象的invalidate()方法
小结:
- Cookie和Session都是来完成一次会话内多次请求间数据共享的
- 区别:
- 存储位置:Cookie是将数据存储在客户端,Session将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大3KB,Session无大小限制
- 存储时间:Cookie可以长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
案例:
用户登录:
Web层
UserMapper
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);
}
Service层
UserService
public class UserService {
SqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();
/**
* 登录方法
* @param username
* @param password
* @return
*/
public User login(String username, String password){
//2. 获取SqlSession
SqlSession sqlSession = factory.openSession();
//3. 获取UserMapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//4. 调用方法
User user = mapper.select(username, password);
//释放资源
sqlSession.close();
return user;
}
}
Dao层
LoginServlet
private UserService service = new UserService();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
//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对象,存储到session
HttpSession session = request.getSession();
session.setAttribute("user", user);
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/selectAllServlet");
}else {
// 登录失败,
// 存储错误信息到request
request.setAttribute("login_msg", "用户名或密码错误");
// 跳转到login.jsp
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
案例:记住用户
- 如果用户勾选"记住用户",则下次访问登陆页面自动填充用户名密码
- 如何自动填充用户名和密码?
- 1.将用户名和密码写入Cookie中,并且持久化存储Cookie,下次访问浏览器会自动携带Cookie
- 2.在页面获取Cookie数据后,设置到用户名和密码框中
- 何时写Cookie?
- 1.登录成功
- 2.用户勾选记住用户复选框
private UserService service = new UserService();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
//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对象,存储到session
HttpSession session = request.getSession();
session.setAttribute("user", user);
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/selectAllServlet");
}else {
// 登录失败,
// 存储错误信息到request
request.setAttribute("login_msg", "用户名或密码错误");
// 跳转到login.jsp
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
案例:记住用户获取Cookie
2.在页面获取Cookie数据后,设置到用户名和密码框中:
${cookie.key.value}//key指存储在cookie中的键名称
<body>
<div id="loginDiv" style="...">
<form action="/brand-demo/loginServlet" 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>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">
<a href="register.html">没有账号?</a>
</div>
</form>
</div>
</body>
案例:用户注册
注册功能:保存用户信息到数据库
验证码功能:
展示验证码:展示验证码图片,并可以点击切换
校验验证码:验证码填写不正确,则注册失败
Dao层
UserMapper
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);
}
Service层
UserService
public boolean register(User user){
//2. 获取SqlSession
SqlSession sqlSession = factory.openSession();
//3. 获取UserMapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//4. 判断用户名是否存在
User u = mapper.selectByUsername(user.getUsername());
if(u == null){
// 用户名不存在,注册
mapper.add(user);
sqlSession.commit();
}
}
Web层
RegisterServlet
@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
private UserService service = new UserService();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
//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);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
this.doGet(request, response);
}
}
}
案例:验证码
注册功能:保存用户信息到数据库
验证码功能:
展示验证码:展示验证码图片,并可以点击切换
校验验证码:验证码填写不正确,则注册失败
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
public class CheckCodeUtil {
// 验证码字符集
private static final String CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
// 验证码长度
private static final int CODE_LENGTH = 4;
// 图片宽度
private static final int IMG_WIDTH = 100;
// 图片高度
private static final int IMG_HEIGHT = 50;
/**
* 生成验证码图片并保存到指定文件,返回验证码字符串
* @param width 图片宽度
* @param height 图片高度
* @param filePath 保存图片的文件路径
* @return 验证码字符串
* @throws IOException 保存图片时可能抛出的异常
*/
public static String outputVerifyImage(int width, int height, String filePath) throws IOException {
String verifyCode = generateVerifyCode();
BufferedImage image = createImage(width, height, verifyCode);
saveImage(image, filePath);
return verifyCode;
}
/**
* 生成随机验证码字符串
* @return 验证码字符串
*/
private static String generateVerifyCode() {
Random random = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < CODE_LENGTH; i++) {
int index = random.nextInt(CHAR_SET.length());
code.append(CHAR_SET.charAt(index));
}
return code.toString();
}
/**
* 创建包含验证码的图片
* @param width 图片宽度
* @param height 图片高度
* @param verifyCode 验证码字符串
* @return 包含验证码的图片
*/
private static BufferedImage createImage(int width, int height, String verifyCode) {
// 创建 BufferedImage 对象
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
// 设置背景颜色
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// 设置字体
g.setFont(new Font("Arial", Font.BOLD, 20));
// 设置文字颜色
g.setColor(Color.BLACK);
// 绘制验证码字符
for (int i = 0; i < verifyCode.length(); i++) {
g.drawString(String.valueOf(verifyCode.charAt(i)), 20 * i + 10, 30);
}
// 绘制干扰线
drawNoiseLines(g, width, height);
g.dispose();
return image;
}
/**
* 绘制干扰线
* @param g 图形上下文
* @param width 图片宽度
* @param height 图片高度
*/
private static void drawNoiseLines(Graphics g, int width, int height) {
Random random = new Random();
g.setColor(Color.GRAY);
for (int i = 0; i < 5; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
g.drawLine(x1, y1, x2, y2);
}
}
/**
* 保存图片到指定文件
* @param image 要保存的图片
* @param filePath 保存图片的文件路径
* @throws IOException 保存图片时可能抛出的异常
*/
private static void saveImage(BufferedImage image, String filePath) throws IOException {
File outputFile = new File(filePath);
ImageIO.write(image, "jpg", outputFile);
}
public static void main(String[] args) {
try {
String filePath = "d://a.jpg";
String checkCode = outputVerifyImage(IMG_WIDTH, IMG_HEIGHT, filePath);
System.out.println("生成的验证码是: " + checkCode);
System.out.println("验证码图片已保存到: " + filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
}
案例:校验验证码
判断程序生成的验证码 和 用户输入的验证码是否一样,如果不一洋,则阻止注册
验证码图片访问和提交注册表单是两次请求,所以要将程序生成的验证码存入Sessio
CheckCodeServlet
将验证码存入session
// 生成验证码
ServletOutputStream os = response.getOutputStream();
String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);
// 存入Session
HttpSession session = request.getSession();
session.setAttribute("checkCodeGen",checkCode);
}
RegisterServlet
1.接收用户输入验证码
2.比对
// 获取用户输入的验证码
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;
}