会话技术
javaWeb中会话是,客户端和服务器之间的一次交互。
打开浏览器,一顿操作后,最后关闭浏览器,就是一个会话。
会话跟踪,用户的数据单独独立的跟随用户的足迹。
有两种方法:浏览器端的cookie 服务器端的 Session会话技术。
Cookie会话技术
把客户的数据放到浏览器端,用来存储一小段数据的。
创建cookie
创建Cookie 唯一的创建方法
Cookie cookie = new Cookie(String key,String value);
创建cookei的构造方法只有一个构造方法
参数都是String类型
- 默认创建的Cookei,到期时间是保存至浏览器关闭
- 保存的路径是当前项目下的路径
通过方法设置
- 保存时间
// 设置时间 0销毁 -1 保存到浏览器关闭 cookie.setMaxAge(0);
- 保存路径
// 设置路径 cookie.setPath("/hui");
获得Cookei
获得的cookei的只能是获得使用,所以是个数组
// 没有获得单个cookei的方法,直接获得所有的cookie Cookie [] cookies = request.getCookies(); for (int i = 0; cookies!=null && i < cookies.length; i++) { Cookie cookie = cookies[i]; // 获得 String name = cookie.getName(); System.out.println("name="+name); String value = cookie.getValue(); System.out.println("value="+value); int maxAge = cookie.getMaxAge(); System.out.println(maxAge); String path = cookie.getPath(); System.out.println(path);
cookei保存中文问题
1.cookie的key不能设置为中文
2.value中文会乱码
cookie 存储中文时如果乱码可以使用
URLEncoder编码,URLDecoder解码
存储编码 :String name URLEncoder.ecoder(“张三”)
取出解码: URLDecoder.decode(name) = 张三
响应给浏览器
resp.addCookie(cookie);
1)Cookie:将数据保存在浏览器客户端的技术
服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了
2)Session:将数据保存在服务端的技术
服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务
Cookie 原理
1)服务端创建cookie对象
2)发送cookie信息到浏览器(HttpServletResponse 中提供void addCookie(Cookie cookie)方法)
例如,响应头中增加 Set-Cookie: name=zhangsan
3)浏览器将得到的cookie信息保存在浏览器端
4)通过浏览器下次访问web应用时,请求数据中会带上cookie信息例如,请求头中 Cookie: name=zhangsan
5)服务器端收到浏览器发送的cookie信息
常用的方法
方法 | |
---|---|
Cookie(String name, String value) | 创建cookie对象的构造方法,value是字符串类型,tomcat8后value可以使用中文 |
String getName() | 取得Cookie的名字 |
String getValue() | 取得Cookie的值 |
void setValue(String newValue) | 设置Cookie的值 |
void setMaxAge(int expiry) | 设置Cookie的最大保存时间,单位秒,即cookie的有效期,默认会话结束,cookie失效。值为 0 时,表示删除该 Cookie |
int getMaxAge() | 获取Cookies的有效期 |
void setPath(String uri) | 设置cookie的有效路径,比如把cookie的有效路径设置为"/aaa",那么浏览器访问"aaa"的web资源时,就会带上cookie |
String getPath() | 获取cookie的有效路径 |
获取Cokie
//获取所有的Cookie Cookie[] cks=request.getCookies(); //遍历Cookie for(Cookie ck:cks){ //检索出自己的Cookie if(ck.getName().equals("code")) { //记录Cookie的值 code=ck.getValue(); break; } }
修改Cookie
只需要保证Cookie的名和路径一致即可修改
//修改Cookie
Cookie ck=new Cookie("code", code);
ck.setPath("/");
ck.setMaxAge(-1);
response.addCookie(ck);//让浏览器添加Cookie
注意:如果改变cookie的name和有效路径会创建cookie而改变cookie值,有效期会覆盖原有cookie
Cookie编码与解码
Cookie默认不支持中文,只能包含ASCII字符,所以Cookie需要对Unicode字符进行编码,否则会出现乱码。
- 编码可以使用java.net.URLEncoder类的encode(String str,String encoding)方法
- 解码使用java.net.URLDecoder类的decode(String str,String encoding)方法
创建带中文Cookie
// 使用中文的 Cookie. name 与 value 都使用 UTF-8 编码.
Cookie cookie = new Cookie(URLEncoder.encode("姓名", "UTF-8"), URLEncoder.encode("二狗子", "UTF-8"));
// 发送到客户端
response.addCookie(cookie);
Cookie优点和缺点
优点
- 可配置到期规则。
- 简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。
- 数据持久性:Cookie默认在过期之前是可以一直存在客户端浏览器上的。
缺点
- 大小受到限制:大多数浏览器对 Cookie 的大小有 4K、8K字节的限制。
- 用户配置为禁用:有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功能。
- 潜在的安全风险:Cookie 可能会被篡改。会对安全性造成潜在风险或者导致依赖于Cookie 的应用程序失败。
Session对象
Session概述
- Session用于记录用户的状态。Session指的是在一段时间内,单个客户端与Web服务器的一连串相关的交互过程。
- 在一个Session中,客户可能会多次请求访问同一个资源,也有可能请求访问各种不同的服务器资源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-icT39I08-1680667313526)(D:\前锋学习笔记\笔记\image-20230405014848837.png)]
Session原理
- 服务器会为每一次会话分配一个Session对象
- 同一个浏览器发起的多次请求,同属于一次会话(Session)
- 首次使用到Session时,服务器会自动创建Session,并创建Cookie存储SessionId发送回客户端
@WebServlet(name = "session1", value = "/s1") public class session1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 创建session // 如果没有,其实就请求cookie中没有JSESSIONId // 就会创建session HttpSession session = request.getSession(); System.out.println("sessionid="+session.getId()); } }
注意:session是由服务端创建的
Session使用
Session作用域:拥有存储数据的空间,作用范围是一次会话有效
- 一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话
- 可以将数据存入Session中,在一次会话的任意位置进行获取
- 可传递任何数据(基本数据类型、对象、集合、数组)
获取Session
session是服务器端自动创建的,通过request对象获取
//获取Session对象
HttpSession session=request.getSession();
System.out.println("Id:"+session.getId());//唯一标记,
Session与Request应用区别
- request是一次请求有效,请求改变,则request改变
- session是一次会话有效,浏览器改变,则session改变
Session 域数据销毁
// 清除session数据 session.removeAttribute("sessionid"); // 销毁session session.invalidate();
拦截器/过滤器(filter)
拦截请求
开发步骤
- 创建类
- 实现Filter接口
- 重写三个方法(init(),doFilterr,destroy())
案例:当用户登录后才可以访问其他的页面,不登录就无法访问其他的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/test" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>个人中心</title>
</head>
<body>
<h1>个人中心</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加用户</title>
</head>
<body>
<h1>用户添加</h1>
</body>
</html>
@WebServlet(name = "test", value = "/test")
public class test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("admin".equals(username) && "123456".equals(password)){
HttpSession session = request.getSession();
session.setAttribute("user",username);
}else {
response.sendRedirect(request.getContextPath()+"/index.html");
}
}
}
拦截器
/**
* @author 二手Java程序员
* @since 2023/4/5 2:38
*/
@WebFilter("/*")
public class Login implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request; // 类型不同需要强制转一下
HttpServletResponse resp = (HttpServletResponse) response; // 类型不同需要强制转一下
String requestURL = req.getRequestURI(); // 获取请求中返回的路径
System.out.println("requsetURL="+requestURL);
if (requestURL.contains("info") || requestURL.contains("add")){ // 判断是请求谁的什么路径
HttpSession session = req.getSession();
Object user = session.getAttribute("user"); // 获取对象
if (user == null){ // 如果对象为空就跳重定向到登录界面
resp.sendRedirect(req.getContextPath()+"/index.html");
}
}
chain.doFilter(request,response);
}
@Override
public void destroy() {
System.out.println("销毁");
}
}
案例二
需求:做登录认证
目前,服务器中有管操作的几个Sevlet
- UserLoginServlet --> /login
- UserAddServlet --> /add
- UserDeleteSerlet --> /delete
这些Servlet 有对呀的映射路径,要保证只有登录后才可以进行注册删除
实现思路:
- 执行登录时,如果登录成功,将登录信息放入session
- 在真正执行注册删除等功能前,先判断有没有登录
- 其实就是判断session中有没有登录的信息
login
@author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/login")
public class UserLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
if ("admin".equals(username)){
HttpSession session = req.getSession();
session.setAttribute("username",username);
System.out.println("登录成功");
}else {
System.out.println("登录失败用户名或密码错误");
}
}
}
add
/**
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/add")
public class UserAddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判断有没有登录
HttpSession session = req.getSession();
Object username = session.getAttribute("username");
if (username != null){
System.out.println("登录成功。。添加用户");
}else {
System.out.println("没有登录,没有操作权限");
}
}
}
delete
/**
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/delete")
public class UserDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判断有没有登录
HttpSession session = req.getSession();
Object username = session.getAttribute("username");
if (username != null){
System.out.println("登录成功。。执行删除操作");
}else {
System.out.println("没有登录,没有操作权限");
}
}
}
lgout 退出
/**
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/logout")
public class UserLgoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判断有没有登录
HttpSession session = req.getSession();
session.invalidate();
System.out.println("退出登录,销毁session");
}
}
使用拦截器、过滤器改造
场景:服务器中有很多处理请求的servlet,项目要求登录认证所以我们每个Servlet执行功能前要对登录信息判断,就会有很多次登录判断的代码编写,重复性能很高!
因为以上操作不高效,代码重复率太高!解决方法就是使用拦截器。
登录
/**![请添加图片描述](https://img-blog.csdnimg.cn/9f195dbbfb4a4f5d861ef388e36fe160.png)
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/login")
public class UserLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("登录成功");
}
}
添加
/**
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/add")
public class UserAddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("添加用户");
}
}
删除
/**
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/delete")
public class UserDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("执行删除操作");
}
}
拦截器
/**
* @author 二手Java程序员
* @since 2023/4/5 11:48
*/
@WebFilter("/login")
public class UserLoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String uri = req.getRequestURI();
if (uri.contains("/login")){
chain.doFilter(request,response);
return;
}
HttpSession session = req.getSession();
Object username = session.getAttribute("username");
if (username != null) { // 不为空就说明已经登录了
chain.doFilter(request,response);
return;
}else {
System.out.println("没有登录,不能操作");
// 可以跳转页面
}
}
@Override
public void destroy() {
}
}
退出销毁session
/**
* @author 二手Java程序员
* @since 2023/4/5 11:14
*/
@WebServlet("/logout")
public class UserLogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判断有没有登录
HttpSession session = req.getSession();
session.invalidate();
System.out.println("退出登录,销毁session");
}
}