1.Filter 过滤器
1.1 filter是Servlet三大技术之一。(servlet、filter、listener)
在请求或响应操作完成之后,要对其进行放行操作,在放行前后可以执行额外操作。
1.2 filter作用
全站乱码过滤
30天自动登录
1.3 filter
1.3.1 filter生命周期
当web应用被加载时,filter会创建一个对象,保留在tomcat内存当中,并且调用init方法进行初始化,如果请求被过滤器拦截,则一定会调用doFilter()方法,在方法内实现请求响应的处理。处理过后可以对request和response选择放行或不放行。如果放行,在放行前后还可以执行额外的操作。在web应用被移出容器的时候filter对象会随之销毁,并且在销毁之前会调用destroy()方法执行善后的操作。
1.3.2 责任链模式
在一个网站中可能会有多个过滤器,所有的请求必须通过全部的过滤器才可以访问到对应的资源。这些过滤器连在一起,组成了一个过滤器的链,这个链称之为责任链模式。filter执行顺序是通过filter-mapping标签进行配置。在web.xml配置文件中,写在前边的filter优先加载。
1.3.4 修改EasyMall项目
思路:现在EasyMall项目中新建一个包,包名为com.easymall.filter,在包中新建一个普通类,实现Filter接口(javax.servlet下的)。然后再web.xml中添加filter配置。(filter中添加filter-name和filter-class,filter-mapping中添加filter-name和url-pattern)。
过滤器一:全站乱码过滤
a. 乱码处理中,响应乱码处理,只需一句话:response.setContentType("text/html;charset=utf-8");
b. 如果是post请求,则只需一句:
request.setCharacterEncoding("utf-8");即可。
c. 如果是get请求,则需要单独对每一个请求参数进行处理。
d. 单独处理每一个请求的中的乱码十分繁琐,所以应该在过滤器中重写获取参数时的方法。
重写的方法有:(涉及到获取参数的都需要修改)
getParameter()
getParameterValues()
getParameterMap()
重写方式:
i. 继承。不可取,修改的是继承后的getParameter方法,原有request对象身上的乱码没有修改。
ii. 装饰者模式。可以。装饰者模式可以通过一个入口对象修改其身上不满意的方法,这种方式修改的是原有request对象身上的方法实现。
iii. 动态代理。可以。
代码实现:
i.乱码过滤器
创建过滤器EncodingFilter:
public class EncodingFilter implements Filter {
boolean encode_use = true;
String encode = "";
public void init(FilterConfig filterConfig) throws ServletException {
//过滤器使用get提交,乱码过滤的开关
boolean encode_use = Boolean.parseBoolean(filterConfig.getServletContext().getInitParameter("encode_use"));
//获取web.xml中的字符集
String encode = filterConfig.getServletContext().getInitParameter("encode");
this.encode_use = encode_use;
this.encode = encode;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//get 请求
MyHttpServletRequest myrequest = (MyHttpServletRequest) (encode_use?new MyHttpServletRequest((HttpServletRequest)request,encode):request);
//post请求
//request.setCharacterEncoding("utf-8");
//响应
response.setContentType("text/html;charset="+encode);
//放行
chain.doFilter(myrequest, response);
}
public void destroy() {}
}
乱码处理类:MyHttpServletRequest
public class MyHttpServletRequest extends HttpServletRequestWrapper{
HttpServletRequest request = null;
String encode = "";
public MyHttpServletRequest(HttpServletRequest request,String encode) {
super(request);
this.request= request;
this.encode = encode;
}
@Override
public Map<String,String[]> getParameterMap() {
//取出原有map中的乱码处理
Map<String,String[]> map = request.getParameterMap();//包含乱码的map
//处理完成放入一个新的map中
Map<String,String[]> rmap = new HashMap<String,String[]>();
//乱码处理
try {
for(Map.Entry<String, String[]> entry:map.entrySet()){
String key = entry.getKey();
String[] values = entry.getValue();
//创建新数组,保存没有乱码的数据
String[] rvalues = new String[values.length];
//遍历处理每一个值的乱码
for(int i=0;i<values.length;i++){
rvalues[i] = new String(values[i].getBytes("iso8859-1"),encode);
}
//将处理后的数组添加到新map中
rmap.put(key, rvalues);
}
//并将新的的map返回
return rmap;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
@Override
public String[] getParameterValues(String name) {
//在不包含乱码的新map中,每一个键都存有一个数组。
//当前方法就是希望通过键获取对应的数组。
//所以获取新map,根据键名,获取值(数组)
Map<String,String[]> map = getParameterMap();
//键名来自参数列表,由用户传入
return map.get(name);
}
@Override
public String getParameter(String name) {
//getParameterValues就是获取一个参数数组,
//取出数组中的第一个元素,即为getParameter所需数据。
String[] value = getParameterValues(name);
return value==null?null:value[0];
}
}
添加配置信息:
<!-- 乱码处理的开关,如果tomcat默认字符集已经是utf-8则此处设置为false -->
<context-param>
<param-name>encode_use</param-name>
<param-value>true</param-value>
</context-param>
<!-- 控制过滤器使用何种编码的配置 -->
<context-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</context-param>
<!-- 全站乱码过滤 -->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.easymall.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
ii.实现30天自动登录
思路:1.由于用户信息需要保存30天,所以这些信息存放在cookie中。(涉及到密码可以使用MD5加密解决)
2.如果用户是未登陆状态则实现自动登录。
3.如果用户是未登录状态,并且包含自动登录cookie,其中的用户名和密码都是正确的,则自动登录。
4.如果用户名或密码错误,不作出任何提示。
5.不论能否登录都需要放行。
代码实现:
i. 创建LoginFilter
//30天自动登录的过滤器
public class LoginFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException { }
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//操作UserService中的方法,所以准备对象
UserService userService = new UserService();
//将ServletRequest强转为HttpServletRequest,其中包含session获取的方法
HttpServletRequest req = (HttpServletRequest) request;
//1.用户是否登录
if(req.getSession(false)==null || req.getSession().getAttribute("user")==null){
//用户没有登录,则需要完成自动登录
//尝试获取自动登录cookie autologin
Cookie autologinC = null;
Cookie[] cookies= req.getCookies();
if(cookies!=null){
for(Cookie c:cookies){
if("autologin".equals(c.getName())){
autologinC = c;
}
}
}
if(autologinC != null){
String value = autologinC.getValue();//username#password
String[] result = value.split("#");
String username = URLDecoder.decode(result[0], "utf-8");
String password = result[1];
//可以直接调用userService中已经实现的loginUser方法实现登录
User user = userService.loginUser(username, password);
//添加到session域保存登录状态
req.getSession().setAttribute("user", user);
}
}
//能否登录都要放行
chain.doFilter(request, response);
}
public void destroy() { }
}
ii. 配置web.xml文件
<!-- 30天自动登录 -->
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.easymall.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
修改LoginServlet
//2.获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String remname = request.getParameter("remname");
String autologin = request.getParameter("autologin");
//记住用户名--cookie实现
if("true".equals(remname)){
Cookie cookie = new Cookie("remname",URLEncoder.encode(username, "utf-8"));
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(60*60*24*30);
response.addCookie(cookie);
}else{
Cookie cookie = new Cookie("remname","");
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
//判断是否选择30天自动登录
//如果选中则创建一个30自动登录cookie
if("true".equals(autologin)){
Cookie cookie = new Cookie("autologin",URLEncoder.encode(username, "utf-8")+"#"+password);
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(60*60*24*30);
response.addCookie(cookie);
}
修改LogOutServlet
//注销servlet
public class LogOutServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if(request.getSession(false)!=null){
//释放session,完成注销
request.getSession(false).invalidate();
//释放cookie,清空30天自动登录
Cookie cookie = new Cookie("autologin","");
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
response.sendRedirect(request.getContextPath());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
2.MD5加密
加密方式:MD5 sha1 sha512
数据摘要加密算法 数据指纹加密算法。
特点:任何内容的数据,经过加密之后,都会形成一个128位的2进制数据,通常写成32位16进制数据。
同一份数据经过MD5加密得到的结果一定是一致。(但是存在极端性。)
经过MD5加密的结果,无法恢复成原有的内容。只能操作的(密文可以变成正文,但是需要较长时间才能计算出结果。)
作用:数据加密,网盘快传。
MD5实现:
a.数据库中数据加密:update user set password=MD5(password);
b. 用户注册信息加密
导入MD5Utils中的内容:
//MD5加密工具类
public class MD5Utils {
/**
* 使用md5的算法进行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
}
修改loginServlet
//*登录功能servlet
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.处理乱码
//request.setCharacterEncoding("utf-8");
//2.获取请求参数
//判断是否选择30天自动登录
//如果选中则创建一个30自动登录cookie
if("true".equals(autologin)){
Cookie cookie = new Cookie("autologin",URLEncoder.encode(username, "utf-8")+"#"+MD5Utils.md5(password));
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(60*60*24*30);
response.addCookie(cookie);
}
UserService userService = new UserService();
try {
User user = userService.loginUser(username,MD5Utils.md5(password));
//此处完成用户登录
request.getSession().setAttribute("user", user);
}
}
修改RegistServlet
import com.mchange.v2.c3p0.ComboPooledDataSource;
//注册servlet
public class RegistServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.请求参数乱码处理
// request.setCharacterEncoding("utf-8");
//1.响应参数乱码处理
// response.setContentType("text/html;charset=utf-8");
//2.获取请求参数
//为用户相关操作做处理的类
UserService userService = new UserService();
//为了传递数据方便,将数据封装在一个javabean中
User user = new User(0,username,MD5Utils.md5(password),nickname,email);
}
3.监听器
3.1监听器概述
listender 是servlet三大技术之一
一旦满足监听器的条件,就会触发对应的操作,执行监听器中的动作。
3.2 Listener监听的分类
JAVAEE开发中,监听器共有三类八种。
a. 监听三大作用域创建和销毁的监听器
ServletContextListener
HttpSessionListener
ServletRequestListener
案例:
public class MyServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext被初始化了。。。"+sce.getServletContext());
}
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext要被销毁了。。。"+sce.getServletContext());
}
}
<listener>
<listener-class>cn.tedu.listener.MyServletContextListener</listener-class>
</listener>
b. 监听三大作用域中属性变化的监听器
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
案例:
public class MySCAtrrListener implements ServletContextAttributeListener {
public void attributeAdded(ServletContextAttributeEvent scab) {
System.out.println("属性被加入sc域"+scab.getName()+"~"+scab.getValue());
}
public void attributeRemoved(ServletContextAttributeEvent scab) {
System.out.println("属性被移除出sc域"+scab.getName()+"~"+scab.getValue());
}
public void attributeReplaced(ServletContextAttributeEvent scab) {
System.out.println("属性被替换在sc域"+scab.getName()+"~"+scab.getValue()+"~"+scab.getServletContext().getAttribute(scab.getName()));
}
}
<listener>
<listener-class>cn.tedu.listener.MySCAtrrListener</listener-class>
</listener>
c. 监听JavaBean在Session域中状态变化的监听器
HttpSessionBindingListener
HttpSessionActivationListener
i. HttpSessionBindingListener
让javabean自己感知到自己被存入或移除出session的状态变化的监听器
此监听器不需要单独开发类,也不需要在web.xml中进行配置,而是直接让需要监听的JavaBean实现即可。
其中具有两个方法:
//当当前javabean的对象被存入session时触发
public void valueBound(HttpSessionBindingEvent event) {
}
//当当前javabean的对象被移除出session时触发
public void valueUnbound(HttpSessionBindingEvent event) {
}
ii. *了解*HttpSessionActivationListener
让javabean自己感知到自己随着session被钝化和活化
3.3 监听器案例
开发监听器,记录应用启动关闭 记录资源被访问 记录用户登录注销 的日志