day17
Servlet Filter 过滤器
day18
Servlet Listener 和 在线支付
day19
JavaMail
day20
文件上传和下载
JavaEE Servlet规范 描述三种技术 Servlet Filter Listener
Servlet 技术 生成动态web资源
Filter 技术 对服务器web资源进行拦截(权限控制)
* Filter 对目标资源拦截,拦截HTTP请求和HTTP响应
* Filter 拦截本质上拦截url访问
Filter接口中定义了三个方法
init 初始化
doFilter 执行过滤
destroy 销毁
* Filter的doFilter方法中,传入FilterChain参数,FilterChain 请求调用链,提供doFilter用于执行调用链下一个资源
编写Servlet步骤
1、继承HttpServlet
2、配置web.xml Servlet虚拟路径
3、覆盖doGet 和 doPost
编写第一个Filter
1、创建hello.jsp ---- http://localhost/day17/hello.jsp
2、编写类 实现Filter 接口 (实现Filter接口 init doFilter destroy )
3、在web.xml中 注册过滤器,配置Filter拦截哪个web资源
在启动服务器时,Filter对象被创建,执行过滤器init 方法
配置拦截后,访问hello.jsp --- 过滤器中doFilter方法获得执行(每请求一次,doFilter 过滤一次)
* 没有配置过滤器之前,直接访问hello.jsp,当配置过滤器后,过滤器先于hello.jsp执行 ----- 此时Filter1和hello.jsp组成 请求调用链FilterChain
* 拦截目标资源后,如果想目标资源执行: chain.doFilter ;如果不执行chain.doFilter 目标资源不会得到执行
destroy方法和Servlet的destroy 一样,在正常关闭服务器情况下执行
* 对同一web资源配置多个过滤器
当配置多个Filter 拦截hello.jsp 组成新的请求调用链 Filter1 --- Filter2 --- hello.jsp
过滤器链会根据mapping 的顺序组成 <filter-mapping> 顺序调用过滤器执行
Filter生命周期三个方法:init doFilter destroy
1、init初始化 ---- 在服务器启动时执行
2、doFilter 执行过滤 ---- 访问目标时,拦截过滤执行,每次请求执行一次
3、destroy 销毁过滤器对象 ----- 在服务器正常关闭时调用
FileConfig 作用和ServletConfig 类似---- 为过滤器提供初始化参数
*String getInitParameter(String name) 获得初始化信息
*public ServletContext getServletContext() 获得ServletContext对象 保存全局数据、读取web资源文件
配置filter-mapping
1、对一个web资源可以配置多个过滤器
2、一个过滤器可以用来过滤多个web 资源
* 在filter-mapping中存在 <servlet-name> 过滤Servlet时,可以通过url 和 name两种配置方式
<url-pattern>/hello</url-pattern>
<servlet-name>HelloServlet</servlet-name>
3、关于url-pattern 写法和Servlet相同 有三种 完全匹配、目录匹配、扩展名匹配
<dispatcher> 标签及意义
配置在什么情况下、在什么调用方式下拦截目标资源
request,forward、include、error 四种方式,服务器调用资源方式
* 默认dispatcher值是request (请求时调用)
request ---- 在请求时过滤
forward ---- 在转发时过滤
include ---- 在包含时过滤
error --- 在错误页面跳转时过滤
web.xml中filter-mapping中配置<dispatcher></dispatcher>默认为request可以配置多个
如果想让过滤器执行,必须满足过滤方式 !!!! 如果forward、include 、error跳转时执行过滤 必须配置 dispatcher
案例一:关于编码集统一处理
功能描述:编写 input.jsp 提供输入姓名表单,在Servlet获得表单数据,将数据打印页面上
分析:过滤器在目标资源之前执行 ,过滤器拦截多个目标资源,多个目标资源相同代码,抽取过滤器中 ----- 经常写代码放入过滤器
request.setCharacterEncoding("utf-8");// 解决请求post乱码
response.setContentType("text/html;charset=utf-8"); // 响应乱码
将以上代码放入EncodingFilter,配置url 为/* 对全站web资源进行编码集设置
复习点:get乱码解决 第一种 配置tomcat server.xml URIEncoding="utf-8" 第二种 new String(xxx.getBytes("ISO-8859-1"),"utf-8");
* 重写getParameter 编写通用解决get乱码程序
* 阅读扩展资料 解决get post 全局乱码过滤器
解决全站get post乱码:
package cn.itcast.filter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* 解决乱码通用的过滤器程序
* @author seawind
*/
public class EncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 解决post
request.setCharacterEncoding("utf-8");
// 解决get
EncodingRequest encodingRequest = new EncodingRequest(
(HttpServletRequest) request);
chain.doFilter(encodingRequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
class EncodingRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
private boolean hasEncode = false;
public EncodingRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
// 通过getParameterMap方法完成
String[] values = getParameterValues(name);
if (values == null) {
return null;
}
return values[0];
}
@Override
public String[] getParameterValues(String name) {
// 通过getParameterMap方法完成
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
@Override
public Map getParameterMap() {
Map<String, String[]> parameterMap = request.getParameterMap();
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
return parameterMap;
}
// get提交方式 手动转码 , 这里的转码只进行一次 所以通过 hasEncode 布尔类型变量控制
if (!hasEncode) {
Set<String> keys = parameterMap.keySet();
for (String key : keys) {
String[] values = parameterMap.get(key);
if (values == null) {
continue;
}
for (int i = 0; i < values.length; i++) {
String value = values[i];
// 解决get
try {
value = new String(value.getBytes("ISO-8859-1"),
"utf-8");
// values是一个地址
values[i] = value;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// 一次转码完成后,设置转码状态为true
hasEncode = true;
}
}
return parameterMap;
}
}
案例二:禁用缓存过滤器
分析:在web开发中,存在很多动态web资源,经常需要更新,为了保证客户端第一时间获得更新后结果,禁止动态web资源缓存
禁止缓存三个头信息
cache-control : no-cache
expires : -1 ---- 日期
pragma : no-cache
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
response.setHeader("Pragma", "no-cache");
* 禁用缓存公共代码放入过滤器中 ------ NoCacheFilter 禁止动态资源缓存
public class NoCacheFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 使用与HTTP协议相关 API,需要将参数转为子类型
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Cache-Control", "no-cache");
httpServletResponse.setDateHeader("Expires", -1);
httpServletResponse.setHeader("Pragma", "no-cache");
chain.doFilter(request, httpServletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
案例三:对于图片一系列资源,不经常发生变动 ----- 图片缓存过滤器
* 虽然tomcat服务器内部 对静态资源有一个缓存策略,但是客户端仍然需要与服务器进行通信,通信性能损失
* 如果一些图片 永远不变,通过Expires 字段,设置图片 缓存时间 (当缓存时间没有过期之前,客户端方位资源 不会与服务器进行通信)
Expires 对于经常不变文件,是一个比tomcat默认缓存策略更优 解决方案!!!
public class ImageExpiresFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 设置图片过期时间 ,设置Expires头信息
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// 过期时间 = 当前时间 + 还有多久过期
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
httpServletResponse
.setDateHeader("Expires", calendar.getTimeInMillis());// 过期时间一个月
httpServletResponse.setDateHeader("Expires", System.currentTimeMillis()
+ 1000L * 60 * 60 * 24 * 30);// 过期时间一个月
chain.doFilter(request, httpServletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
--------------------------------------------------------------------------------------------------------------------------------
案例四:自动登陆过滤器
功能:第一次登陆时,勾选自动登陆,关闭浏览器,再次打开,系统会自动进行用户已经登陆状态
登陆功能
create table user(
id int primary key auto_increment,
username varchar(40),
password varchar(40),
role varchar(40)
);
insert into user values(null,'aaa','111','admin');
insert into user values(null,'bbb','111','user');
insert into user values(null,'ccc','111','user');
* 复制mysql驱动、c3p0、dbutils 到WEB-INF/lib 下
* 复制JDBCUtils 工具类 和c3p0-config.xml
转发和重定向使用
1、使用转发,通过request.setAttribute 传递数据给 页面,路径不要写/工程名 ;
2、使用重定向,不能用request传递数据,路径 要写 /工程名
AutoLoginFilter 在什么情况下需要自动登陆 ??
1、当前用户必须未登陆
2、存在自动登陆cookie信息
* 自动登陆过滤器,过滤页面一定不包括登陆和注册 页面
自动登陆案例问题:
1、 如果用户名中文怎么办?
insert into user values(null,'小红','123','user');
解决方案:保存cookie时进行手动编码 URL编码
生成cookie 时 new Cookie(URLEncoder.encode(用户名,"utf-8")); ------ LoginServlet
读取cookie时, 对用户进行解码 URLDecoder.decode(用户名,"utf-8"); ---- AutoLoginFilter
2、 中文名和密码安全问题
* cookie 是一个文本类型文件,内容非常容易泄露
实际开发中数据库中密码和cookie中密码 都应该 加密后保存 ---- 单向加密算法 MD5
* MySQL 提供md5 方法进行加密
将数据进行md5 加密
update user set password = md5(password) ;
MD5 破解 ----- 多对一映射算法 (运行两个不同明文 产生相同密文 概率很低) ---- 王小云 并没有提供解密算法,提供一套提高碰撞几率算法
* http://www.cmd5.com/
Base64 编解码技术,用于加密网络传输中安全内容 ---- 例如:迅雷下载地址
* 很多协议内容传输都采用BASE64 编码
public class DigestUtils {
public static void main(String[] args) {
// 迅雷地址获得 --- 使用Base64
String url = "www.itcast.cn/爱情公寓1.rmvb";
// 在地址前后添加 AA和ZZ
url = "AA" + url + "ZZ";
// 进行base64编码
url = base64encode(url);
// 在路径前 thunder://
url = "thunder://" + url;
System.out.println(url);
}
/**
* Base64编码
*/
public static String base64encode(String message) {
BASE64Encoder base64Encoder = new BASE64Encoder();
return base64Encoder.encode(message.getBytes());
}
/**
* Base64解码
*
* @throws IOException
*/
public static String base64decode(String message) throws IOException {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] buf = base64Decoder.decodeBuffer(message);
return new String(buf);
}
/**
* 使用md5的算法进行加密
*
* @param plainText
* 加密原文
* @return 加密密文
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
return new BigInteger(1, secretBytes).toString(16);// 将加密后byte数组
// 转换16进制表示
}
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 1、判断当前用户是否已经登陆
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (httpServletRequest.getSession().getAttribute("user") == null) {
// 未登录
// 2、判断autologin对应cookie 是否存在 ---- 将cookie 查询写为工具类
Cookie cookie = CookieUtils.findCookie(httpServletRequest
.getCookies(), "autologin");
if (cookie != null) {
// 找到了自动登陆cookie
String username = cookie.getValue().split("\\.")[0];// 如果用户名中文,需要解密
username = URLDecoder.decode(username, "utf-8");
String password = cookie.getValue().split("\\.")[1];
// 获得就是md5 加密密码,此处无需加密
// 登陆逻辑
String sql = "select * from user where username=? and password = ?";
Object[] args = { username, password };
QueryRunner queryRunner = new QueryRunner(JDBCUtils
.getDataSource());
try {
User user = queryRunner.query(sql, new BeanHandler<User>(
User.class), args);
if (user != null) {
httpServletRequest.getSession().setAttribute("user",
user);
}
chain.doFilter(httpServletRequest, response);
} catch (SQLException e) {
e.printStackTrace();
}
} else {
// 没有自动登陆信息
chain.doFilter(httpServletRequest, response);
}
} else {
// 已经登陆,不需要自动登陆
chain.doFilter(httpServletRequest, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获得用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// MD5 加密
password = DigestUtils.md5(password);
// 查询数据库
String sql = "select * from user where username = ? and password= ?";
Object[] args = { username, password };
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
try {
User user = queryRunner.query(sql,
new BeanHandler<User>(User.class), args);
if (user == null) {
// 查询用户不存在,登陆失败
request.setAttribute("msg", "用户名或者密码错误");
request.getRequestDispatcher("/demo4/login.jsp").forward(
request, response);
} else {
// 登陆成功
// 判断是否勾选自动登陆
if ("true".equals(request.getParameter("autologin"))) {
// 用户勾选了 自动登陆
// 将用户名和密码 以cookie形式写给客户端
Cookie cookie = new Cookie("autologin", URLEncoder.encode(
username, "utf-8")
+ "." + password);
cookie.setMaxAge(60 * 60);
cookie.setPath("/day17");
response.addCookie(cookie);
}
// 将成功信息 保存Session
request.getSession().setAttribute("user", user); // 表示已经登陆
response.sendRedirect("/day17/demo4/welcome.jsp");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
案例五:关于URL级别权限控制
一个软件系统功能非常多,将功能进行分类(根据用户角色),将不同功能放入不同url 目录中 ,对目录级别进行url 权限控制
/public/ 代表游客可以访问 : 用户注册、用户登录
/user/ 代表普通用户可以访问: 购买商品,查看购物车,订单支付
/admin 代表超级管理员可以访问:查看商品销售情况,添加商品
建立JSP,在index.jsp提供对八个功能jsp访问 链接
编写登陆功能
仍然使用 day17 数据库 下 user 表
* 权限控制时,登陆功能游客可以访问 ----- 已经将login.jsp 放入public 但是 LoginServlet 也是登陆程序 也路径放入public
* LoginServlet 配置url-pattern 必须为/public/login
编写PrivilegeFilter 对资源进行全局权限控制
* 重点:
1、 获得访问资源路径
String path = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length());
2、 判断当前用户是否登陆
User user = (User) httpServletRequest.getSession().getAttribute("user");
if (user == null) {
// 未登陆
} else {
// 已经登陆
}
* 过滤掉未登录不能访问资源
3、获得用户权限,判断权限可以访问哪些资源 user.getRole()
今天学习重点内容:
1、过滤器原理
2.过滤器5个案例
案例一、案例二、案例三 重点原理 --- 代码很少
案例四 自动登陆 (原理) --- 会写
案例五 URL级别权限控制 (重点!!!!!!)