Servlet实践-留言版-v2
功能介绍:
为留言板-v1添加登录功能
如何实现登录功能:
1.在doGet()中检查会话中是否包含了username特性,若包含了,则表示已经登录,将重定向到留言板列表页面。若不存在,则设置请求特性loginFailed为false(也就是说不是因为登录不匹配而导致登录失败),将请求转发到登录页面。
2.当用户以post的方式提交登录表单时,在doPost()方法中将用户提交的登录信息与数据库中的登录信息比较,若一致,就重定向到留言板列表页面;若不一致,则设置请求特性loginFailed为true,将请求转发到登录页面
如何实现注销功能:
获取HttpSession对象session,若包含请求参数logout,则调用session.invalidate()使当前会话无效,并重定向至登录页面。
使用监听器检测会话的变化:
使用HttpSessionListener和HttpSessionIdListener监听器,它们会捕获会话事件。
使用注解、编程或者部署描述符中声明等方式注册监听器
重写相应的方法:
1.当会话创建时,将调用sessionCreated(HttpSessionEvent e)方法
2.当会话无效时,将调用sessionDestoryed(HttpSessionEvent e)方法
3.当使用请求的changeSessionId()方法改变会话ID时将调用sessionIdChanged()方法
维护活跃会话列表:
在SessionRegistry类中,维护了一个静态的Map(以会话ID为键,以对应的会话对象为值),当创建会话、销毁会话、更新会话ID时,对应地在这个map中添加新会话对象、移除对应会话对象、更新会话对象。
loginServlet.java
package cn.example; import java.io.IOException; import java.util.Hashtable; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet( name ="loginServlet", urlPatterns = "/login" ) public class LoginServlet extends HttpServlet{ // 创建一个用户数据库 private static final Map<String, String> userDatabase = new Hashtable<String, String>(); static{ userDatabase.put("Nicholas", "password"); userDatabase.put("Sarah", "drowssap"); userDatabase.put("Mike", "wordpass"); userDatabase.put("John", "green"); } // 显示登录界面 @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); // 添加注销功能 if(request.getParameter("logout") != null){ session.invalidate(); response.sendRedirect("login"); return; }else if(session.getAttribute("username") != null){ // 检测用户是否已经登录(username特性是否存在),若已经登录,就他们重定向至票据页面 response.sendRedirect("tickets"); return; } // 未登录,将请求特性中的loginFailed设置为false,将请求转发到登录界面 request.setAttribute("loginFailed", false); request.getRequestDispatcher("/WEB-INF/jsp/view/login.jsp").forward(request, response); } // 处理登录信息 @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 检测用户是否已经登录(username特性是否存在),若已经登录,就他们重定向至票据页面 HttpSession session = request.getSession(); if(session.getAttribute("username") != null){ response.sendRedirect("tickets"); return; } String username = request.getParameter("username"); String password = request.getParameter("password"); // 登录失败,设置请求特性loginFailed为true if(username == null || password == null || !LoginServlet.userDatabase.containsKey(username) || !password.equals(LoginServlet.userDatabase.get(username))){ request.setAttribute("loginFailed", true); request.getRequestDispatcher("/WEB-INF/jsp/view/login.jsp").forward(request, response); }else{ // 登录成功,把用户名存放在session中 session.setAttribute("username", username); request.changeSessionId(); response.sendRedirect("tickets"); } } }
SessionListener.java
package cn.example; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionIdListener; import javax.servlet.http.HttpSessionListener; @WebListener public class SessionListener implements HttpSessionListener, HttpSessionIdListener{ private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void sessionIdChanged(HttpSessionEvent e, String oldSessionId) { SessionRegistry.updateSessionId(e.getSession(), oldSessionId); System.out.println(this.date() + ": Session ID " + oldSessionId + " changed to " + e.getSession().getId()); } @Override public void sessionCreated(HttpSessionEvent e) { SessionRegistry.addSession(e.getSession()); System.out.println(this.date() + ": Session " + e.getSession().getId() + " created." ); } private String date() { return this.formatter.format(new Date()); } @Override public void sessionDestroyed(HttpSessionEvent e) { SessionRegistry.removeSession(e.getSession()); System.out.println(this.date() + ": Session " + e.getSession().getId() + " destoryed."); } }
SessionRegistry.java
package cn.example; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import javax.servlet.http.HttpSession; /* * 一个注册表,用来保存活跃会话的引用 */ public final class SessionRegistry { private static final Map<String, HttpSession> SESSIONS = new Hashtable<String, HttpSession>(); public static void addSession(HttpSession session){ SESSIONS.put(session.getId(), session); } public static void updateSessionId(HttpSession session, String oldSessionId){ synchronized (SESSIONS) { SESSIONS.remove(oldSessionId); addSession(session); } } public static void removeSession(HttpSession session){ SESSIONS.remove(session.getId()); } public static List<HttpSession> getAllSession(){ return new ArrayList<HttpSession>(SESSIONS.values()); } public static int getNumberOfSessions(){ return SESSIONS.size(); } private SessionRegistry(){} }
SessionListSertvlet.java
package cn.example; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * 显示会话 */ @WebServlet( name = "sessionListServlet", urlPatterns = "/sessions" ) public class SessionListServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 若用户没有登录,就重定向到登录界面 if(request.getSession().getAttribute("username") == null){ response.sendRedirect("login"); return; } request.setAttribute("numberOfSessions", SessionRegistry.getNumberOfSessions()); request.setAttribute("sessionList", SessionRegistry.getAllSession()); request.getRequestDispatcher("/WEB-INF/jsp/view/sessions.jsp").forward(request, response); } }
login.jsp
<!DOCTYPE html> <html> <head> <title>留言板</title> </head> <body> <h2>Login</h2> 你需要登录到留言板<br/> <br/> <% if((Boolean)request.getAttribute("loginFailed")){ %> <b>用户名或密码错误</b><br/><br/> <% } %> <form method="post" action="<c:url value="/login"/> "> 用户名:<br> <input type="text" name="username"/> <br/><br/> 密码:<br/> <input type="password" name="password" /> <br/></br/> <input type="submit" value="Log In"/> </form> </body> </html>
session.jsp
<%@ page import="java.util.List" %> <%! private static String toString(long timeInterval){ if(timeInterval < 1_000) return "less than one second"; if(timeInterval < 60_000) return (timeInterval / 1_000) + " seconds"; return "about " + (timeInterval / 60_000) + " minutes"; } %> <% int numberOfSessions = (Integer) request.getAttribute("numberOfSessions"); List<HttpSession> sessions = (List<HttpSession>) request.getAttribute("sessionList"); %> <!DOCTYPE html> <html> <head> <title>留言板/title> </head> <body> <a href="<c:url value="/login?logout"/>">Logout</a> <h2>Sessions</h2> There are a total of <%= numberOfSessions %> active sessions in this application.<br/> <br/> <% long timestamp = System.currentTimeMillis(); for(HttpSession aSession : sessions){ out.print(aSession.getId() + " - " + aSession.getAttribute("username")); if(aSession.getId().equals(session.getId())) out.print(" (you)"); out.print("- last active " + toString(timestamp - aSession.getLastAccessedTime())); out.print(" ago<br/>"); } %> </body> </html
listTickets.jsp:
<%@ page session="false" import="java.util.Map" %> <% @SuppressWarnings("unchecked") Map<Integer,Ticket> ticketDatabase = (Map<Integer, Ticket>)request.getAttribute("ticketDatabase"); %> <!DOCTYPE html> <html> <head> <title>留言板</title> </head> <body> <h2>留言板</h2> <a href="<c:url value="/login?logout"/>">Logout</a><br/> <a href=" <c:url value="/tickets"> <c:param name="action" value="create"/> </c:url> ">创建留言</a><br/><br/> <% if(ticketDatabase.size() == 0){ %><i>留言板中没有留言。</i><% } else{ for(int id : ticketDatabase.keySet()){ String idString = Integer.toString(id); Ticket ticket = ticketDatabase.get(id); %>留言 #<%= idString %> : <a href=" <c:url value="/tickets"> <c:param name="action" value="view"/> <c:param name="ticketId" value="<%= idString %>"/> </c:url> "><%=ticket.getSubject() %></a>(用户: <%= ticket.getCustomerName() %>) <br/> <% } } %> </body> </html>
ticketForm.jsp
<%@ page session="false" %> <!DOCTYPE html> <html> <head> <title>留言板</title> </head> <body> <a href="<c:url value="/login?logout"/>">Logout</a><br/> <h2>创建留言</h2> <form method="post" action="tickets" enctype="multipart/form-data"> <input type="hidden" name="action" value="create"/> 主题:<br/> <input type="text" name="subject"><br/><br/> 内容:<br/> <textarea name="body" rows="5" cols="30"></textarea><br/><br/> <b>附件:</b><br/> <input type="file" name="file1" /><br/><br/> <input type="submit" value="提交"/> </form> </body> </html>
viewTicket.jsp
<% String ticketId = (String) request.getAttribute("ticketId"); Ticket ticket = (Ticket) request.getAttribute("ticket"); %> <!DOCTYPE html> <html> <head> <title>留言版</title> </head> <body> <a href="<c:url value="/login?logout"/>">Logout</a><br/> <h2>留言 #<%=ticketId %>: <%= ticket.getSubject() %></h2> <i>用户 - <%=ticket.getCustomerName() %></i> <br/><br/> <i>内容:</i><br/> <%= ticket.getBody() %> <br/><br/> <% if(ticket.getNumberOfAttachments() > 0){ %>附件:<% int i = 0; for(Attachment a:ticket.getAttachments()){ if(i++ > 0) out.print(", "); %> <a href=" <c:url value="/tickets"> <c:param name="action" value="download"/>\ <c:param name="ticketId" value="<%= ticketId %>"/> <c:param name="attachment" value="<%=a.getName() %>"/> </c:url> "><%=a.getName() %> </a><% } } %><br/> <a href="<c:url value="/tickets"/>">返回留言板主页</a> </body> </html>