会话跟踪技术:
会话跟踪是Web程序中常⽤的技术,⽤来跟踪⽤户的整个会话。保持对⽤户会话期间的数据管理。常⽤
的会话跟踪技术是Cookie与Session。
Cookie通过在客户端记录信息确定⽤户身份
Session通过在服务器端记录信息确定⽤户身份。
Cookie是客户端(⼀般指浏览器)请求服务器后,服务器发给客户端的⼀个辨认标识,保存在客户端,当客户端再次向服务器发送请求时,会携带着这个辨认标识,服务器就可以通过这个标识来识别客户端的身份或状态等。
Cookie的作⽤:跟踪会话,记录⼀次会话中(即Session,⼀次会话可能会有多次请求,当然也可以有多个Cookie来跟踪不同的信息)的信息,这样服务器就会知道⽤户的状态,⽐如有没有登录成功,付款时购物⻋中的东⻄等,就相当于贴在客户端脑⻔上的纸条,浏览器看不到,但服务器看得到。
将⽤户的信息保存到Cookie中,并发送给浏览器,并且将有效时间设置为⼀个较⻓的时间,这样浏览器在以后访问⽹站时,都会带着该Cookie,服务器以此来辨识⽤户,⽤户就不再需要输⼊⽤户名和密码等信息。
⼀旦⽤户登录成功以后,下次再登录时,直接将Cookie中的⽤户名读取并显示出来,这样⽤户就不需要再次输⼊⽤户名,只输⼊密码即可。
注意cookie由于保存在本地,因此是不安全的,最好保存一些不敏感的数据,比如用户名。密码最好保存在服务器。
发送方设置cookie:
Cookie cookie = new Cookie(name, value);
response.addCookie(cookie);
接收方取得cookies:
Cookie[] cookies = request.getCookies();
当用户登录失败时,通过cookie记录用户名,并立刻返回到登录页面。此时登录页的用户名已经被自动填写好。
在servlet中修改doPost方法:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String userName = req.getParameter("name");
String userPass = req.getParameter("pass");
try {
boolean f = new DatabaseValidation().validate(userName, userPass);
if(f){
req.getSession().setAttribute("userName", userName);
req.getSession().setAttribute("userPass", userPass);
resp.sendRedirect("success.jsp");
}else{
Cookie cookie = new Cookie("uname", userName);
cookie.setMaxAge(30);
resp.addCookie(cookie);
resp.sendRedirect("login.jsp");
}
} catch (ClassNotFoundException|SQLException e) {
e.printStackTrace();
}
}
在login.jsp中得到cookie并且取得里面的userName的值。
<%
Cookie[] cookies = request.getCookies();
String str = "";
if(cookies != null && cookies.length > 0){
for(Cookie cookie: cookies){
if(cookie.getName().equals("uname")){
str = cookie.getValue();
break;
}
}
}
pageContext.setAttribute("mycookie", str);
%>
...
<input class="form-control" type="text" name="name" id="userName" value="${mycookie}"/>
Cookie发送给浏览器以后,浏览器并不会永久保存,也就是到了⼀定的时间以后浏览器会⾃动销毁
Cookie。Cookie的默认有效时间为⼀次会话(⼀次打开关闭浏览器的过程),我们也可以⼿动指定Cookie
的有效时间。
设置cookie的有效时间:cookie.setMaxAge(秒数);
删除cookie:通过设置cookie的有效时间=0即可。
Session保存在服务器端。为了获得更⾼的存取速度,服务器⼀般把Session放在内存⾥。每个⽤户都会
有⼀个独⽴的Session。如果Session内容过于复杂,当⼤量客户访问服务器时可能会导致内存溢出。因
此,Session⾥的信息应该尽量精简。
Session在⽤户第⼀次访问服务器的时候⾃动创建。需要注意只有访问JSP、Servlet等程序时才会创建
Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未⽣成Session,也可以使
request.getSession(true)强制⽣成Session。
Session⽣成后,只要⽤户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。⽤户
每访问服务器⼀次,⽆论是否读写Session,服务器都认为该⽤户的Session“活跃(active)”了⼀次。
由于会有越来越多的⽤户访问服务器,因此Session也会越来越多。为防⽌内存溢出,服务器会把⻓时间
内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服
务器,Session就⾃动失效了。
Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过
setMaxInactiveInterval(long interval)修改。
Session的超时时间也可以在web.xml中修改。另外,通过调⽤Session的invalidate()⽅法可以使Session
失效。
过滤器实际上就是对web资源进⾏拦截,做⼀些处理后再交给下⼀个过滤器或servlet处理,通常都是⽤来拦截request进⾏处理的,也可以对返回的response进⾏拦截处理。
我们重点关注的是doFilter方法。doFilter方法接收ServletRequest和ServletResponse作为参数,这些是我们自定义过滤规则的基础。此外还有一个filterChain对象,调用filterChain.doFilter(servletRequest, servletResponse)可以直接调用下一个filter。如果没有下一个filter则请求发送给servlet执行。
@WebFilter()
进行注册。
@WebFilter(urlPatterns = "/test")
,表示对于/test
请求将走此过滤器。@WebFilter(servletNames = "com.kkb.xzk.servlet.MyServlet")
,表示发送到此servlet的请求将首先被此过滤器过滤。注意:
- 可以设置
urlPatterns = "/*"
表示过滤所有请求。- 一旦修改了web.xml文件,必须要重启服务器才可生效。注意修改了注解@WebFilter等效于修改了web.xml,也需要重启服务器生效。
思路:因为登录后我们会在session中设置userName属性,因此在过滤器里通过request.getSession()得到session,再判断session中是否有userName属性来判断用户是否已经登录。如果没登录则自动跳转到登录页面。
定义Filter如下
@WebFilter(urlPatterns = "/*")
public class FirstFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器1初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器1调用");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
String userName = (String) session.getAttribute("userName");
String uri = request.getRequestURI();
if(uri.endsWith("success.jsp") && userName==null){
response.sendRedirect("login.jsp");
}
//调用下一个过滤器,如果没有过滤器则调用servlet。
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("过滤器1结束");
}
@Override
public void destroy() {
System.out.println("过滤器1销毁");
}
}
过滤器的特点:在servlet之前和之后都会被执⾏
监听器就是监听某个域对象的的状态变化的组件。
监听器的相关概念:
servletContext对应的就是application作用域。
第⼀维度按照被监听的对象划分:ServletRequest域、HttpSession域、ServletContext域
第⼆维度按照监听的内容分:监听域对象的创建与销毁的、监听域对象的属性变化的
ServletContext域 | HttpSession域 | ServletRequest域 | |
---|---|---|---|
域对象创建、销毁 | ServletContextListener | HttpSessionListener | ServletRequestListener |
域对象属性变化 | ServletContextAttributeListener | HttpSessionAttributeListener | ServletRequestAttributeListener |
记住域名就够了,如果只监听域对象创建和销毁,就是
域名+Listener
;如果要检测属性变化,就是域名+AttributeListener
。
实现Listener接口
检测HttpSession属性变化的监听器:
public class SessionAttributeListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("session属性值增加:name=" + httpSessionBindingEvent.getName() + ",value=" + httpSessionBindingEvent.getValue());
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("session属性值删除:name=" + httpSessionBindingEvent.getName() + ",value=" + httpSessionBindingEvent.getValue());
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("session属性值修改:name=" + httpSessionBindingEvent.getName() + ",value=" + httpSessionBindingEvent.getValue());
}
}
HttpSession创建和销毁的监听器:
public class SessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("session创建了");
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("session销毁了");
}
}
在web.xml中通过
<listener>
<listener-class>com.kkb.xzk.listener.SessionListenerlistener-class>
listener>
<listener>
<listener-class>com.kkb.xzk.listener.SessionAttributeListenerlistener-class>
listener>