标签:ref blank package 无效 应用 名称 UI 结构 检查
参考谷歌翻译,关键字直接使用英文,原文地址:http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url-rewriting
Java Web应用程序中的会话管理(Session Management)是一个非常有趣的话题。Java Servlet中的会话通过不同的方式进行管理,例如Cookie,HttpSession API,URL重写等。
这是Java Web应用程序系列教程中的第三篇文章,您可能还需要先阅读两篇文章。
本文旨在使用不同的技术和示例程序解释servlet中的会话管理。
HTTP协议和Web服务器都是无状态的,这意味着对Web服务器而言,每个请求都是一个新的进程,它无法确定请求是否来自先前发送过请求的客户端。
但有时候,我们需要知道客户端是谁,并且相应地处理请求。例如,购物车应用程序应该知道谁正在发送添加商品的请求,谁正在发送结帐的请求,以便可以无误地进行收费或者把商品添加到正确的购物车中。
Session是客户端和服务器之间的会话状态,它可以由客户端和服务器之间的多个请求和响应组成。由于HTTP和Web服务器都是无状态的,维护会话的唯一方法是在每个请求和响应中传递关于会话的唯一信息(session id)。
我们可以通过以下几种方式在请求和响应中提供唯一的标识符。
这就是为什么我们需要Session Management API,以及为什么J2EE技术附带了Session Management API供我们使用。
在Web应用程序中Cookie应用得很多,它可以根据你的选择来个性化响应或跟踪会话。在转到Session Management API之前,我想通过一个小型Web应用程序来展示如何利用Cookie跟踪会话。
我们将创建一个具有如下图所示项目结构的动态Web应用程序ServletCookieExample。
(原文ide使用的是Eclipse,使用Intellij Idea的朋友可以参考Intellij Idea 创建Web项目入门 by 我是一名老菜鸟)
web应用程序的部署描述文件web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>ServletCookieExample</display-name> <welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list> </web-app>
我们的应用程序的欢迎页面是login.html,我们将从用户那里获取身份验证的详细信息。
<!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <title>Login Page</title> </head> <body> <form action="LoginServlet" method="post"> Username: <input type="text" name="user"> <br> Password: <input type="password" name="pwd"> <br> <input type="submit" value="Login"> </form> </body> </html>
这是一个负责处理“登录请求”的LoginServlet。
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "Pankaj"; private final String password = "journaldev"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ Cookie loginCookie = new Cookie("user",user); //setting cookie to expiry in 30 mins loginCookie.setMaxAge(30*60); response.addCookie(loginCookie); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Either user name or password is wrong.</font>"); rd.include(request, response); } } }
请注意我们设置在response中的cookie,它在之后将被转发到LoginSuccess.jsp,此cookie将用于跟踪会话。还要注意,“cookie超时”设置为30分钟。理想情况下,应该有一个复杂的逻辑来设置跟踪会话的cookie值,以便它不会与别的请求相冲突。
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% String userName = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); } } if(userName == null) response.sendRedirect("login.html"); %> <h3>Hi <%=userName %>, Login successful.</h3> <br> <form action="LogoutServlet" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
请注意,如果尝试直接访问JSP,它将自动把我们转到“登录页面”。当我们点击注销按钮时,应该确保cookie已经从浏览器中删除。
package com.journaldev.servlet.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class LogoutServlet */ @WebServlet("/LogoutServlet") public class LogoutServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); Cookie loginCookie = null; Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")){ loginCookie = cookie; break; } } } if(loginCookie != null){ loginCookie.setMaxAge(0); response.addCookie(loginCookie); } response.sendRedirect("login.html"); } }
没有方法可以删除cookie,但是我们可以将cookie的maximum age设置为0,它将立刻被浏览器移除。
当我们运行上面的应用程序时,将得到如下响应。
Servlet API通过Http Session接口提供会话管理。我们可以使用以下方法从HttpServletRequest对象中获取session。HttpSession允许我们将对象设置为属性,以便在将来的请求中能够检索。
HttpSession的一些重要方法是:
getAttributeNames()
, removeAttribute(String name)
和 setAttribute(String name, Object value)
.getLastAccessedTime()
方法获得最后访问的时间。getMaxInactiveInterval()
方法获取“session超时值”。当使用HttpServletRequest的getSession()方法时,我们将获得一个新的HttpSession对象,与此同时,一个特殊的Cookie将被添加到对应的Response对象,这个Cookie名字叫做JSESSIONID,它的值就是“session id”。这个cookie用于在客户端的进一步请求中识别HttpSession对象。如果Cookie在客户端被禁用,并且我们正在使用URL重写,则该方法使用request URL中的JSESSIONID值来查找相应的会话。JSESSIONID cookie用于会话跟踪,因此我们不应该将其用于我们的应用程序,以避免造成任何会话相关的问题。
让我们看看使用HttpSession对象的会话管理示例。我们将在Eclipse中创建一个动态Web项目,项目结构如下图所示。
login.html与前面的示例一样,并定义应用程序的欢迎页面。
LoginServlet servlet将创建session,并在其中设置我们可以在其他资源或将来请求中使用的属性。
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ HttpSession session = request.getSession(); session.setAttribute("user", "Pankaj"); //setting session to expiry in 30 mins session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("user", user); userName.setMaxAge(30*60); response.addCookie(userName); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Either user name or password is wrong.</font>"); rd.include(request, response); } } }
我们的LoginSuccess.jsp 如下所示。
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% //allow access only if session exists String user = null; if(session.getAttribute("user") == null){ response.sendRedirect("login.html"); }else user = (String) session.getAttribute("user"); String userName = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); if(cookie.getName().equals("JSESSIONID")) sessionID = cookie.getValue(); } } %> <h3>Hi <%=userName %>, Login successful. Your Session ID=<%=sessionID %></h3> <br> User=<%=user %> <br> <a href="CheckoutPage.jsp">Checkout Page</a> <form action="LogoutServlet" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
当使用JSP资源时,容器会自动为其创建一个session,所以我们不能检查会话是否为空,来确保用户是否通过登录页面,因此我们使用session的属性来验证请求。
CheckoutPage.jsp是另外一个页面,它的代码如下所示。
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% //allow access only if session exists if(session.getAttribute("user") == null){ response.sendRedirect("login.html"); } String userName = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); } } %> <h3>Hi <%=userName %>, do the checkout.</h3> <br> <form action="LogoutServlet" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
我们的LogoutServlet代码如下所示。
package com.journaldev.servlet.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LogoutServlet */ @WebServlet("/LogoutServlet") public class LogoutServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("JSESSIONID")){ System.out.println("JSESSIONID="+cookie.getValue()); break; } } } //invalidate the session if exists HttpSession session = request.getSession(false); System.out.println("User="+session.getAttribute("user")); if(session != null){ session.invalidate(); } response.sendRedirect("login.html"); } }
请注意,我在日志中打印了JSESSIONID cookie的值,你可以检查服务器日志在哪里打印了与LoginSuccess.jsp中的Session id相同的值。
下图显示了我们的Web应用程序的执行。
正如我们在上一节中看到的,我们可以使用HttpSession来管理一个会话,但是如果我们在浏览器中禁用了cookies,它将不起作用,因为服务器将不会从客户端收到JSESSIONID cookie。Servlet API提供了对URL重写的支持,以便我们可以在这种情况下管理会话。
从编码的角度来看,它很容易使用,它涉及到了一个步骤 - 编码URL(encoding the URL)。编码URL的另一个好处是它是一种“后备方法”,只有在浏览器Cookie被禁用的情况下才会启用。
我们可以使用HttpServletResponse的 encodeURL()
方法对URL进行编码,如果我们必须将请求重定向到另一个资源,并且提供会话信息,那么我们可以使用encodeRedirectURL()
方法。
我们将创建一个类似的项目,这个项目将使用URL重写来确保会话管理工作正常(即使浏览器禁用了cookies)。
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ HttpSession session = request.getSession(); session.setAttribute("user", "Pankaj"); //setting session to expiry in 30 mins session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("user", user); response.addCookie(userName); //Get the encoded URL string String encodedURL = response.encodeRedirectURL("LoginSuccess.jsp"); response.sendRedirect(encodedURL); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Either user name or password is wrong.</font>"); rd.include(request, response); } } }
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% //allow access only if session exists String user = null; if(session.getAttribute("user") == null){ response.sendRedirect("login.html"); }else user = (String) session.getAttribute("user"); String userName = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); if(cookie.getName().equals("JSESSIONID")) sessionID = cookie.getValue(); } }else{ sessionID = session.getId(); } %> <h3>Hi <%=userName %>, Login successful. Your Session ID=<%=sessionID %></h3> <br> User=<%=user %> <br> <!-- need to encode all the URLs where we want session information to be passed --> <a href="<%=response.encodeURL("CheckoutPage.jsp") %>">Checkout Page</a> <form action="<%=response.encodeURL("LogoutServlet") %>" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% String userName = null; //allow access only if session exists if(session.getAttribute("user") == null){ response.sendRedirect("login.html"); }else userName = (String) session.getAttribute("user"); String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); } } %> <h3>Hi <%=userName %>, do the checkout.</h3> <br> <form action="<%=response.encodeURL("LogoutServlet") %>" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
package com.journaldev.servlet.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LogoutServlet */ @WebServlet("/LogoutServlet") public class LogoutServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("JSESSIONID")){ System.out.println("JSESSIONID="+cookie.getValue()); } cookie.setMaxAge(0); response.addCookie(cookie); } } //invalidate the session if exists HttpSession session = request.getSession(false); System.out.println("User="+session.getAttribute("user")); if(session != null){ session.invalidate(); } //no encoding because we have invalidated the session response.sendRedirect("login.html"); } }
当我们在禁用Cookie的状况下运行此项目时,将显示如下响应页面,请注意浏览器地址栏URL中的jsessionid。另外请注意,在LoginSuccess页面上,用户名是null,因为浏览器没有发送最后一个响应所发送的cookie。
如果Cookie未被禁用,你将不会在URL中看到jsessionid,因为Servlet Session API会在这种情况下使用cookie。
原文地址:http://www.journaldev.com/1907/java-session-management-servlet-httpsession-url-rewriting
Java中的会话管理——HttpServlet,Cookies,URL Rewriting(译)
标签:ref blank package 无效 应用 名称 UI 结构 检查
原文地址:http://www.cnblogs.com/xkxf/p/6942192.html