`

day17过滤器 禁止缓存中文乱码自动登录MD5加密url级别权限控制

阅读更多

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级别权限控制 (重点!!!!!!)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics