您的位置:首页 > 大数据

大数据WEB阶段(十五)JavaEE三大核心技术之过滤器

2017-12-11 21:51 591 查看

Filter过滤器

一、Filter 过滤器概述

Filter是JavaEE三大核心技术(Servlet 、 Filter 、 Listener)之一

FIlter作用是拦截对资源的访问 , 拦截下来后可以控制是否允许通过 , 或者在允许通过前后做一些额外的操作 。

所谓的拦截其实就是对代表请求的request、对象和代表 响应的response对象拦截下来 , 进行控制

一个过滤器可能拦截多个资源 , 一个资源也可能被多个过滤器拦截

这种多个拦截器拦截一个资源的模式成为责任链模式 。


常用场景:

就与URL的访问权限控制

全站乱码解决过滤器

过滤敏感词汇

压缩响应

二、 过滤器的开发

想要开发一个过滤器 , 需要两个步骤

写一个类实现Filter接口

在web.xml中配置过滤器

Filter接口:

init(FilterConfig)

初始化的方法 , 当Filter被初始化时 , 调用此方法 , 执行初始化操作

destory()

销毁方法, 在Filter被销毁之前调用 , 执行善后操作

doFilter(ServletRequest request , ServletResponse response , FilterChain chain)

核心方法 ,在存活期间 , 过滤器拦截到对资源的访问 会造成此方法的执行 , 需要在这个方法中设计过滤器的核心逻辑代码

配置过滤器

<filter> -- 配置一个过滤器
<filter-name>FirstFilter</filter-name> -- 过滤器的名字
<filter-class>com.tarena.filter.FirstFilter</filter-class> -- 过滤器的类
</filter>
<filter-mapping> -- 过滤器的拦截路径配置,可以配置多个
<filter-name>FirstFilter</filter-name> -- 为哪个名字的过滤器配置
<url-pattern>/*</url-pattern> -- 拦截哪个路径资源可以配置多个
<servlet-name>XxxServlet</servlet-name> -- 拦截哪个名字的Servlet
<dispatcher></dispatcher> -- 指定过滤器拦截哪种方式对资源的访问,可以取值为REQUEST FORWARD INCLUDE ERROR,如果不配置,默认只拦截REQUEST方式的访问。可以配置多个。
</filter-mapping>


三、生命周期

在web应用启动时 , 会创建处web应用中配置的过滤器对象 , 创建出过滤器对象会立即调用init方法进行初始化操作 , 之后一直存活 , 直到web应用被销毁时 , Filter跟着被销毁.在销毁之前会自动调用destory方法执行善后操作 。 在存活期间 , 每当拦截到资源访问 , 就执行doFilter方法 , 来执行过滤器的 逻辑 , 如果不做操作 , 则 默认拦截 , 可以通过FilterChain类的对象的 doFilter方法实现对资源访问的放行 。 并且可以在doFilter前后做一些操作 。

四、 细节

如果一个资源被多个过滤器拦截 , 多个 拦截器的拦截顺序取决于在web.xml文件中配置过滤器时的先后顺序 。

多个 过滤器的执行 , 类似于方法 一层一层调用的过程 ,, 一层一层往里钻, 然后在一层层一层往外出 。



五、 和Filter开发相关的对象

FilterConfig:

init方法的参数

代表FIlter在web.xml文件中的配置对象

可以用来获取Filter在Web.xml文件中的初始化配置参数

可以用来获取ServletContext对象

public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init....");

//1.filterConfig功能1:获取filter的初始化参数
Enumeration<String> names = filterConfig.getInitParameterNames();
while(names.hasMoreElements()){
String name = names.nextElement();
String value = filterConfig.getInitParameter(name);
System.out.println(name+"~"+value);
}

//2.获取ServletContext对象
ServletContext sc = filterConfig.getServletContext();

}


FilterChain:

doFilter方法参数

代表过滤器链

提供了doFilter方法 ,放行当前过滤器 , 执行后续过滤器 , 如果后续没有过滤器则调用到相应的资源 。

六、Filter案例

全站乱码解决过滤器

在web开发过程中 , 存在请求参数乱码和响应输出乱码 。

之前的开发中 , 在所有的Servlet和jsp页面中 , 需要手动解决这两种乱码

可以通过开发过滤器拦截所有的资源访问 , 在过滤器中解决全站乱码问题 。

具体解决请求响应乱码问题:

1. 在web.xml文件 中配置全局的编码类型
<!-- 全局配置 -->
<context-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</context-param>
2. 在过滤器初始化时获取全局配置的编码 , 并保存到过滤器中
private String encode = null;
public void init(FilterConfig config) throws ServletException {
encode = (String) config.getServletContext().getAttribute("encode");
}

3. 解决响应乱码:
在doFilter方法中
response.setCharacterEncoding(encode);
response.setContentType("text/html;charset="+encode);
4. 解决请求参数乱码
//方案一:
//      request.setCharacterEncoding(encode); //只能解决Post请求参数的乱码
//可以解决Post和Get请求类型的参数乱码
//但是在转码的时候需要指定具体的参数名称  ,转码之后要重新放入request中供servlet拿取更是不能实现 , 所以 不可行
//      String param = new String(request.getParameter("xxxx").getBytes("iso-8859-1") , encode);
//方案二:
//request中的请求参数本身无法改变
//那么 换一个思路 想办法改造和获取请求参数相关的方法 在方法内加上解决乱码的代码
//这样通过这些方法获取请求参数时 解决好乱码再返回 用起来就感觉 乱码被解决了一样
//改造原有request方法方案一:
//继承
//继承只能先改造在创建实例 , 但是现在已经有了request对象,  就算通过继承改造了ServletRequest类也不会影响到已有的对象 , 排除
//改造原有request方法方案二:
//装饰设计模式
//1. 新建一个类 , 实现与被改造对象相同的接口
//2. 通过狗仔方法传入被改造的对象并保存在本类中
//3. 然后实现接口中所有的方法 , 如果需要改造则在对应的方法里写出逻辑 , 如果不需要改造的方法 ,则直接通过传入的没被改造的参数对象调用原有的方法即可
//这种方案 的缺点 ,如果被改造的方法 中方法过多时 ,这个操作会十分繁琐 。
//改造原有的request对象方案三:
//在装饰设计模式的基础上实现
//通过源码 发现Servlet包下已经提供了一个ServletRequestWrapper类 , 它实现了与ServletRequest相同的接口
//也就是说他就是java中已经提供的供开发者修改request对象中方法的入
4000
口
//新建一个类继承ServletRequestWrapper类之后 ,通过构造方法 把原始对象传进去 , 然后只重写需要改造的方法即可
代码//
public class EncodeFilter implements Filter{
/**
* 当前web应用编码集
*/
private String encode = null;

/**
* 初始化方法
*/
public void init(FilterConfig filterConfig) throws ServletException {
//获取ServletContext对象
ServletContext sc = filterConfig.getServletContext();
//读取初始化参数 中的 编码集 配置
this.encode = sc.getInitParameter("encode");
}

/**
* 过滤方法
*/
public void doFilter(final ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
//--全站响应乱码
response.setCharacterEncoding(encode);
response.setContentType("text/html;charset="+encode);
//--全站请求乱码 - 通过装饰器 装饰request 修改其中的和获取请求参数相关的方法 增加了乱码解决的代码
ServletRequest myReq = new MyServletRequest((HttpServletRequest) request);
//放行资源
chain.doFilter(myReq, response);
}

/**
* 销毁方法
*/
public void destroy() {

}

/**
* 内部类 ServletRequest的装饰类 改造了获取请求参数相关的方法 增加了乱码解决的代码
*/
//继承了HttpServletRequestWrapper ,这个父类本身就是 HttpServletRequest的装饰器  在其中提供方法的默认的实现 不想改造的方法 不用管 想改造的方法 覆盖父类方法即可
class MyServletRequest extends HttpServletRequestWrapper{

private ServletRequest request = null;

private boolean hasNotEncode = true;

//构造器 接受传入的request保存在类的内部
public MyServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
}

//覆盖和获取请求参数相关的方法
@Override
public Map<String,String[]> getParameterMap() {
try {
//1.获取真正request的请求参数组成的map
Map<String,String[]>  map = request.getParameterMap();
if(hasNotEncode){//由于真正的request对此map会缓存 所以解决乱码的操作 只需要做一次 此处通过hasNotEncode来控制
//2.遍历map
for(Map.Entry<String, String[]>entry : map.entrySet()){
//3.获取当前遍历到的值的数组
String [] values = entry.getValue();
//4.遍历值的数组
for(int i = 0;i<values.length;i++){
//5.解决乱码 存回数组
values[i] = new String(values[i].getBytes("iso8859-1"),encode);
}
}
hasNotEncode = false;
}
//6.返回解决完乱码的map
return map;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}


用户30天内自动登录

在处理用户登录时 , 判断用户是否勾选30天内自动登录 , 如果用户名密码正确且勾选过该选项 , 则发送cookie , 将用户名密码保存30天。 为了安全起见 , 保存之前先对密码进行MD5加密

之后用户在来访问时经过自动登录过滤器被拦截 , 如果用户未登录 , 且带了自动登录的cookie , 并且其中的用户名密码都正确 , 则给给用户给自动登录 。但是无论自动登录与否都要对url放行 。

在LoginServlet中添加用户自动登录逻辑: 如果用户勾选 了自动登录 , 则将用户信息添加进cookie中保存在本地

在AutoLoginFilter中 拦截所有请求 , 先判断是否登录 , 在判断是否有自动登录cookie ,最后判断用户密码是否正确

自动登录过滤器源码

public class AutoLoginFilter implements Filter{

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
//      1. 判断该用户是否 已经登录
if(req.getSession(false) == null ||req.getSession(false).getAttribute("user") == null){
System.out.println("用户未登录");
//      2. 判断访问时是否带有自动登录cookie
Cookie[] cookies = req.getCookies();
for(Cookie c :cookies){
if("autologin".equals(c.getName())){
System.out.println("带有自动登录cookie");
String v = c.getValue();
String[] vs = URLDecoder.decode(v , "utf-8").split("#");
UserService us = BaseFactory.getBase().getInstance(UserService.class);
//      3. 验证用户名密码是否正确
User user = us.login(vs[0], vs[1]);
if(user != null){
System.out.println("开始登录");
//三个条件都满足 , 添加登录标记
req.getSession().setAttribute("user", user);
System.out.println("自动登录成功");
}
break;
}
}
}
//      4. 无论是否自动 登录成功 , 都放行访问。  放行访问
chain.doFilter(request , response);
}

public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub

}

}


七、MD5加密算法

又称数据摘要算法 , 数据指纹算法

任意长度的二进制文件计算出128位二进制的再要信息 , 通常转换为32位16进制显示 。

明文相同算出的密文一定相同

明文不同算出的密文一定不同 (概率极低 , 所以一般认为是唯一的)

只能由明文算成密文 ,, 不能有密文算成明文

应用:

加密存储数据

文件完整性校验

数字签名
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐