今日内容
零、 复习昨日
一、Cookie
二、Session
三、拦截器
四、登录认证、全局编码格式
零、 复习昨日
注解
热部署
请求转发
重定向
路径问题
- 总结使用经验: 无论请求路径是多层是单层,在写路径时都从/开始,即从根开始
- 如果是服务器动作,从/开始直接写
- 如果是浏览器动作,从/开始,需要再加上项目名
一、状态管理
1.1 现有问题
HTTP协议是无状态的,不能保存每次提交的信息
如果用户发来一个新的请求,服务器无法知道它是否与上次的请求有联系。
对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。
1.2 概念
将浏览器与web服务器之间多次交互当作一个整体来处理,并且将多次交互所涉及的数据(即状态)保存下来。
1.3 状态管理分类
客户端
状态管理技术:将状态保存在客户端。代表性的是Cookie
技术。
服务器
状态管理技术:将状态保存在服务器端。代表性的是session
技术(服务器传递sessionID时需要使用Cookie的方式)
二、Cookie的使用
2.1 什么是Cookie
Cookie是在浏览器访问Web服务器的某个资源时,由Web
服务器
在HTTP响应消息头
中附带传送给浏览器
的一小段数据
。一旦Web浏览器保存了某个Cookie,那么它在以后每次访问该Web服务器时,都应在HTTP请求头中将这个Cookie回传给Web服务器。
一个Cookie主要由标识该信息的名称(name)和值(value)组成。
Cookie原理 |
---|
2.2 创建Cookie
// 在服务器中
Cookie cookie = new Cookie(key,value);
// 响应出去到浏览器
resp.addCookie(cookie)
2.3 获取cookie
// 从请求中获得cookie.一次获得全部的cookie对象
// 但是一次获得当前站点所有cookie对象,没有获得单独某一个cookie
Cookie[] cookies = req.getCookies();
for (int i = 0; cookies != null && i < cookies.length; i++) {
Cookie ck = cookies[i];
String name = ck.getName( );
String value = ck.getValue( );
System.out.println(name+"="+value );
}
2.4 删除cookie
- 没有删除的方法
- 主要靠最大生命值来确定
- 默认保存至会话结束(浏览器关闭)
- 也可以通过在创建时指定生命周期时间
- setMaxAge
- 大于0,存在指定的秒
- 等于0,直接删除
- 小于0,一般是-1 ,保存至浏览器关闭
// 创建cookie
Cookie cookie = new Cookie("username","admin");// 中文会乱码
// 创建的cookie,默认存储到浏览器关闭.
// 可以设置时间,单位是秒!!
// cookie.setMaxAge(30);
// cookie.setMaxAge(0); // 直接删除该cookie
cookie.setMaxAge(-1); // 保存至浏览器关闭
2.5 修改Cookie
只需要保证Cookie的名和路径一致即可修改
//修改Cookie
Cookie ck=new Cookie("code", code);
ck.setPath("/webs");//设置Cookie的路径
ck.setMaxAge(-1);
response.addCookie(ck);//让浏览器添加Cookie
- 注意:如果改变cookie的name和有效路径会新建cookie, 而改变cookie值、有效期会覆盖原有cookie
2.6 设置cookie的路径
所谓路径,其实就是cookie中数据保存的位置
Cookie不设置路径,默认路径是当前项目下
设计两个Servlet,来读取CookieServlet1设置的cookie
一个叫UserServlet,映射路径/user/ck,读取cookie
一个叫AdminServlet,映射路径/admin/ck,读取cookie
演示1:
CookieServlet1设置的cookie的路径默认是http://localhost/day40/
通过http://localhost/day40/user/ck 能读取到cookie值
通过http://localhost/day40/admin/ck 能读取到cookie值
演示2
CookieServlet1设置的cookie的路径为是==/day40/user==
通过http://localhost/day40/user/ck 能读取到cookie值
通过http://localhost/day40/admin/ck 能读取不到cookie值
// 设置cookie路径
cookie.setPath("/day40/user");
总结: cookie设置路径,其实就是设置cookie的读取范围
2.7 中文乱码解决
// 中文编码 URLEncoded.encoded("value","UTF-8")
// 将乱码解码成中文 URLDecode.decode(value,"UTF-8")
// cookie中文解决
// jdk自带有将中文编码和解码的方法
Cookie cookie = new Cookie(
URLEncoder.encode("姓名","UTF-8"),
URLEncoder.encode("张三","UTF-8"));
for (int i = 0; cookies != null && i < cookies.length; i++) {
Cookie ck = cookies[i];
String name = ck.getName( );
String value = ck.getValue( );
// 解码
String u = URLDecoder.decode(name, "UTF-8");
String v = URLDecoder.decode(value, "UTF-8");
System.out.println(u+"="+v );
}
2.8 Cookie优点和缺点
2.8.1 优点
可配置到期规则。
简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。
数据持久性:Cookie默认在过期之前是可以一直存在客户端浏览器上的。
2.8.2 缺点
大小受到限制:大多数浏览器对 Cookie 的大小有 4K、8K字节的限制。
用户配置为禁用:有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功能。、
潜在的安全风险:Cookie 可能会被篡改。会对安全性造成潜在风险或者导致依赖于Cookie 的应用程序失败。
三、Session对象【重点
】
3.1 Session概述
- Session用于记录用户的状态。Session指的是在一段时间内,单个客户端与Web服务器的一连串相关的交互过程。
- 在一个Session中,客户可能会多次请求访问同一个资源,也有可能请求访问各种不同的服务器资源。
3.2 Session原理
服务器会为每一次会话分配一个Session对象
同一个浏览器发起的多次请求,同属于一次会话(Session)
首次使用到Session时,服务器会自动创建Session,并创建Cookie存储SessionId发送回客户端
- 注意:session是由服务端创建的。
3.3 Session使用
- Session作用域:拥有存储数据的空间,作用范围是一次会话有效
- 一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话
- 可以将数据存入Session中,在一次会话的任意位置进行获取
- 可传递任何数据(基本数据类型、对象、集合、数组)
3.3.1 获取Session
session是服务器端自动创建的,通过request对象获取
//获取Session对象
HttpSession session=request.getSession();
System.out.println("Id:"+session.getId());//唯一标记,
3.3.2 Session保存数据
setAttribute(属性名,Object)保存数据到session中
session.setAttribute("key",value);//以键值对形式存储在session作用域中。
ps: 登录后将登录信息存储到session
3.3.3 Session获取数据
getAttribute(属性名);获取session中数据
session.getAttribute("key");//通过String类型的key访问Object类型的value
ps: 后续其他请求获取session,判断是否有对应的权限或者是否登录
3.3.4 Session移除数据
removeAttribute(属性名);从session中删除数据
session.removeAttribute("key");//通过键移除session作用域中的值
3.3.5 Session失效
session.setMaxInactiveInterval(60*60);//设置session最大有效期为一小时
session.invalidate();//手工销毁
ps: 退出登录后,清空session
3.4 Session与Request应用区别
- request是一次请求有效,只有一次请求转发内数据可以获得
- session是一次会话内有效,无论请求转发还是重定向都是可以获得数据的
3.5 模拟登录认证
现有2个资源,一个是登录/login,一个是/add,没有登录认证之前,可以不用登录就可以访问/add所对应的资源,但是不合理,因此我们需要实现登录信息的认证!
即在操作/add等其他请求时我们需要判断这次请求前有没有登录,如果没有登录就响应(请求转发)到登录页面,如果登录过,那就该干什么干什么…
如何实现?
- 需要在登录成功时,将登录信息存入session
- 需要在操作/add等请求时先判断session有无登录信息
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 登录成功,session存储数据
HttpSession session = req.getSession( );
session.setAttribute("username","tomcat");
System.out.println("登录...");
}
}
@WebServlet("/add")
public class AddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判断有无登录
HttpSession session = req.getSession( );
String username = (String) session.getAttribute("username");
if (username != null && !"".equals(username)) {
// 假设这下面就是操作的功能,用输出语句代替,,,
System.out.println("添加...");
} else {
// 没有登录,应该跳转到登录页面
System.out.println("没有登录,无法操作..." );
}
}
}
四、过滤器Filter【重点
】
4.1 现有问题
在以往的Servlet中,有没有冗余的代码,多个Servlet都要进行编写。
4.2 概念
过滤器(Filter)是处于客户端与服务器目标资源之间的一道过滤技术。
过滤器 |
---|
4.3 过滤器作用
执行地位在Servlet之前,客户端发送请求时,会先经过Filter,再到达目标Servlet中;响应时,会根据执行流程再次反向执行Filter
可以解决多个Servlet共性代码的冗余问题(例如:乱码处理、登录验证)
4.4 编写过滤器
Servlet API中提供了一个Filter接口,开发人员编写一个Java类实现了这个接口即可,这个Java类称之为过滤器(Filter)
4.4.1 实现过程
编写Java类实现Filter接口
在doFilter方法中编写拦截逻辑
设置拦截路径
4.5 过滤器配置
4.5.1 注解配置
在自定义的Filter类上使用注解@WebFilter(value=“/过滤目标资源”)
4.5.2 xml配置
<!--过滤器的xml配置 -->
<filter>
<!--名称-->
<filter-name>sf</filter-name>
<!--过滤器类全称-->
<filter-class>com.qf.web.filter.SecondFilter</filter-class>
</filter>
<!--映射路径配置-->
<filter-mapping>
<!--名称-->
<filter-name>sf</filter-name>
<!--过滤的url匹配规则和Servlet类似-->
<url-pattern>/*</url-pattern>
</filter-mapping>
4.5.3 过滤器路径
过滤器的过滤路径通常有三种形式:
精确过滤匹配 ,比如/index.jsp /myservlet1
后缀过滤匹配,比如*.jsp、*.html、*.jpg
通配符过滤匹配/*,表示拦截所有。注意过滤器不能使用/匹配。
/aaa/bbb/* 允许
4.6 过滤器链和优先级
4.6.1 过滤器链
客户端对服务器请求之后,服务器调用Servlet之前会执行一组过滤器(多个过滤器),那么这组过滤器就称为一条过滤器链。
每个过滤器实现某个特定的功能,当第一个Filter的doFilter方法被调用时,Web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则Web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
过滤器链 |
---|
4.6.2 过滤器优先级
在一个Web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
优先级:
如果为注解的话,是按照类全名称的字符串顺序决定作用顺序
如果web.xml,按照 filter-mapping注册顺序,从上往下
web.xml配置高于注解方式
如果注解和web.xml同时配置,会创建多个过滤器对象,造成过滤多次。
4.7 过滤器应用1:全局编码
以前: 在每个servlet为了防止乱码,在进行操作之前,都先设置请求和响应的编码格式.
现在: 使用拦截器,对所有请求拦截器,设置编码,然后放行
package com.qf.servlet.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc 对所有请求都是设置编码格式
*/
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 设置编码格式
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 放行
chain.doFilter(request,response);
}
@Override
public void destroy() { }
}
4.8 过滤器应用2: 身份认证拦截器
以前: 在web项目中,在每个Servlet中都要对身份进行认证
现在: 使用拦截器,将身份过滤的代码前置到拦截器,对每个请求先身份认证
package com.qf.servlet.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
@WebFilter("/*")
// @WebFilter("/hw/*")
// @WebFilter("*.do")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
/**
* 对请求的身份进行认证
* 请求中获得session,且session中有登录时存入的数据,说明登录成功,放行
* 请求中获得session,但是session中没有数据,说明没有登录,重定向到登录页
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 获取session
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession( );
String requestURI = req.getRequestURI( );
/**
* 因为是拦截所有 /*
* 所以要放行 静态资源以及第一次登录的请求
*/
if(requestURI.contains("/login") ||requestURI.contains("html") || requestURI.contains("js") || requestURI.contains("css")) {
// 放行
chain.doFilter(request, response);
}
// 从session获得数据
String username = (String) session.getAttribute("username");
// 判断
if (username != null){
// 放行
chain.doFilter(request, response);
} else {
HttpServletResponse resp = (HttpServletResponse) response;
resp.sendRedirect(req.getContextPath()+"/index.html");
}
}
@Override
public void destroy() { }
}