与Session的亲密接触&彻底掌握Java中Session Token Cookie

Session管理


  • Session管理
  • 网址重写
  • 隐藏域
  • cookie
  • Servlet相对路径和绝对路径

Session管理(Session追踪)是Web应用程序开发中一个非常重要的主题.
本篇文章将重点讲解常用的Session管理的四个方法:网址重写 隐藏域 cookieHttpSession对象.

1.网址重写

网址重写是一种Session追踪技术,需要将一个或多个token作为一个查询字符串添加到一个URL中.token的格式是键=值.
URL和token之间要用一个问号(?)隔开,两个token之间用一个&符号隔开.
如果token不必在过多的URL中四处携带,那么网址重写比较合适.

它的缺点如下:
- 有的浏览器中,URL限制2000个字符.
- 仅当有链接要插入值时,值才能转换成后面的资源.此外,不容易将值插入到静态网页中.
- 网址重写必须在服务器端有效,所有链接都必须带有值,可能造成一个页面会有许多链接.
- 空格 &符号及问号都必须进行编码.
- URL中的信息是明显可见的.

网址重写栗子:

package app02a.urlrewriting;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
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 = "Top10Servlet", urlPatterns = { "/top10" })
public class Top10Servlet extends HttpServlet {
    private static final long serialVersionUID = 987654321L;

    private List londonAttractions;
    private List parisAttractions;

    @Override
    public void init() throws ServletException {
        londonAttractions = new ArrayList(10);
        londonAttractions.add("Buckingham Palace");
        londonAttractions.add("London Eye");
        londonAttractions.add("British Museum");
        londonAttractions.add("National Gallery");
        londonAttractions.add("Big Ben");
        londonAttractions.add("Tower of London");
        londonAttractions.add("Natural History Museum");
        londonAttractions.add("Canary Wharf");
        londonAttractions.add("2012 Olympic Park");
        londonAttractions.add("St Paul's Cathedral");

        parisAttractions = new ArrayList(10);
        parisAttractions.add("Eiffel Tower");
        parisAttractions.add("Notre Dame");
        parisAttractions.add("The Louvre");
        parisAttractions.add("Champs Elysees");
        parisAttractions.add("Arc de Triomphe");
        parisAttractions.add("Sainte Chapelle Church");
        parisAttractions.add("Les Invalides");
        parisAttractions.add("Musee d'Orsay");
        parisAttractions.add("Montmarte");
        parisAttractions.add("Sacre Couer Basilica");
    }

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {

        String city = request.getParameter("city");
        if (city != null && 
                (city.equals("london") || city.equals("paris"))) {
            // show attractions
            showAttractions(request, response, city);
        } else {
            // show main page
            showMainPage(request, response);
        }
    }

    private void showMainPage(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("" +
                "Top 10 Tourist Attractions" +
                "" + ""
                "Please select a city:" +
                "
London"
+ "
Paris"
+ ""); } private void showAttractions(HttpServletRequest request, HttpServletResponse response, String city) throws ServletException, IOException { int page = 1; String pageParameter = request.getParameter("page"); if (pageParameter != null) { try { page = Integer.parseInt(pageParameter); } catch (NumberFormatException e) { // do nothing and retain default value for page } if (page > 2) { page = 1; } } List attractions = null; if (city.equals("london")) { attractions = londonAttractions; } else if (city.equals("paris")) { attractions = parisAttractions; } response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("" + "Top 10 Tourist Attractions" + ""); writer.println("Select City "); writer.println("
Page "
+ page + "
"
); int start = page * 5 - 5; for (int i = start; i < start + 5; i++) { writer.println(attractions.get(i) + "
"
); } writer.print("
"
+ "Page 1"); writer.println(Page 2"); writer.println(""); } }

最值得关注的是a标签的href属性值,它包含一个问号,接着是token:city=london或着city=paris.任何相对的URL(没有协议部分的URL)都会被当作是相对于当前页面的URL,如果点击页面的某一个超链接,那么:

http://localhost:8080/app02a/top10?city=london

或者

http://localhost:8080/app02a/top10?city=paris

就会被发送到服务器.
运行效果:
初始页:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第1张图片
伦敦前十大旅游地中的第一页:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第2张图片
伦敦十大旅游地的第二页:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第3张图片

隐藏域

利用隐藏域来保存状态,与采用网址重写技术类似.只是它不把值放到URL后面,而是放到HTML表单的隐藏域中.
优点是: 将更多的字符传到服务器,不需要进行字符编码.
缺点是:只有当要传递的信息不需要跨越多个页面时,才适合使用这个技术.

隐藏域栗子:
Customer类构建一个客户模型:

package app02a.hiddenfields;
public class Customer {
    private int id;
    private String name;
    private String city;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}

利用隐藏域来更新客户信息:

package app02a.hiddenfields;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * Not thread-safe. For illustration purpose only
 */
@WebServlet(name = "CustomerServlet", urlPatterns = { 
        "/customer", "/editCustomer", "/updateCustomer"})
public class CustomerServlet extends HttpServlet {
    private static final long serialVersionUID = -20L;

    private List customers = new ArrayList();

    @Override
    public void init() throws ServletException {
        Customer customer1 = new Customer();
        customer1.setId(1);
        customer1.setName("Donald D.");
        customer1.setCity("Miami");
        customers.add(customer1);

        Customer customer2 = new Customer();
        customer2.setId(2);
        customer2.setName("Mickey M.");
        customer2.setCity("Orlando");
        customers.add(customer2);       
    }

    private void sendCustomerList(HttpServletResponse response)
            throws IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("Customers"
                + "

Customers

"
); writer.println("
    "); for (Customer customer : customers) { writer.println("
  • " + customer.getName() + "(" + customer.getCity() + ") (" + "edit)"); } writer.println("
"
); writer.println(""); } private Customer getCustomer(int customerId) { for (Customer customer : customers) { if (customer.getId() == customerId) { return customer; } } return null; } private void sendEditCustomerForm(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); int customerId = 0; try { customerId = Integer.parseInt(request.getParameter("id")); } catch (NumberFormatException e) { } Customer customer = getCustomer(customerId); if (customer != null) { writer.println("" + "Edit Customer" + "

Edit Customer

"
+ "
+ "action='updateCustomer'>"); writer.println(""); writer.println(""); writer.println(""); writer.println(""); writer.println("" + "" + ""); writer.println(""); writer.println("
Name:" + ", "'") + "'/>
City:" + ", "'") + "'/>
" + "
" + "Customer List" + "
"
); writer.println(""
); } else { writer.println("No customer found"); } } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uri = request.getRequestURI(); if (uri.endsWith("/customer")) { sendCustomerList(response); } else if (uri.endsWith("/editCustomer")) { sendEditCustomerForm(request, response); } } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // update customer int customerId = 0; try { customerId = Integer.parseInt(request.getParameter("id")); } catch (NumberFormatException e) { } Customer customer = getCustomer(customerId); if (customer != null) { customer.setName(request.getParameter("name")); customer.setCity(request.getParameter("city")); } sendCustomerList(response); } }

运行效果:
客户列表:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第4张图片
Edit Customer表单:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第5张图片
注意到表单中的隐藏域没有?它包含了customer id,因此当提交表单时,服务器就会知道正在编辑哪个客户的信息

writer.println(“< input type=’hidden’ name=’id’ value=’” + customerId + “’/>”);

cookie

网址重写和隐藏域都只试用于保持那些不需要跨越许多页面的信息.
cookie是自动地在web服务器与浏览器之间来回传递的一小块信息,需要根据自己的需要来设置cookie的有效期.每台web服务器最多可以支持20个cookie.
它存在于javax.servlet.http.Cookie类

Cookie的创建方法:

Cookie cookie=new Cookie(name,value);

创建Cookie之后,要设置它的domain path maxAge属性,尤其maxAge它是Cookie的有效期.
之后需要用HttpServletResponse调用add方法,将Cookie发送到浏览器.
使用HttpServletResponse调用getCookies方法获得Cookie它返回的是Cookie数组,如果没有Cookie将返回NULL.

获取Cookie栗子:

Cookie[] cookies = request.getCookies();
        Cookie maxRecordsCookie = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("maxRecords")) {
                    maxRecordsCookie = cookie;
                    break;
                }
            }
        }

很遗憾,没有像getCookieByName这样的方法直接获取Cookie,更闹心的是,也没有直接删除Cookie的方法.要删除Cookie需要创建一个同名的Cookie,将它的maxAge属性设置为0,并在HttpServletResponse中添加一个新的cookie.

删除Cookie栗子:

Cookie cookie = new Cookie("userName", "");
cookie.setMaxAge(0);
response.addCookie(cookie);

接下来写个完整使用Cookie的栗子:

PreferenceServlet类:
该类的作用是允许用户修改4个cookie的值,从而在同一个应用程序中设定其他Servlet的显示设置

package app02a.cookie;
import java.io.IOException;
import java.io.PrintWriter;
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;

@WebServlet(name = "PreferenceServlet", 
        urlPatterns = { "/preference" })
public class PreferenceServlet extends HttpServlet {
    private static final long serialVersionUID = 888L;

    public static final String MENU = 
            "
" + "Cookie Class  " + "Cookie Info  " + "Preference" + "
"
; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.print("" + "Preference" + "" + "" + MENU + "Please select the values below:" + "
" + "" + "" + "" + "" + "" +"" + "" + "" + "" + "" + "" + "" + "
Title Font Size:
Title Style & Weight:
Max. Records in Table:
" + "
"
+ ""
+ ""); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String maxRecords = request.getParameter("maxRecords"); String[] titleStyleAndWeight = request .getParameterValues("titleStyleAndWeight"); String titleFontSize = request.getParameter("titleFontSize"); response.addCookie(new Cookie("maxRecords", maxRecords)); response.addCookie(new Cookie("titleFontSize", titleFontSize)); // delete titleFontWeight and titleFontStyle cookies first // Delete cookie by adding a cookie with the maxAge = 0; Cookie cookie = new Cookie("titleFontWeight", ""); cookie.setMaxAge(0); response.addCookie(cookie); cookie = new Cookie("titleFontStyle", ""); cookie.setMaxAge(0); response.addCookie(cookie); if (titleStyleAndWeight != null) { for (String style : titleStyleAndWeight) { if (style.equals("bold")) { response.addCookie(new Cookie("titleFontWeight", "bold")); } else if (style.equals("italic")) { response.addCookie(new Cookie("titleFontStyle", "italic")); } } } response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("" + "Preference" + "" + MENU + "Your preference has been set." + "

Max. Records in Table: "
+ maxRecords + "
Title Font Size: "
+ titleFontSize + "
Title Font Style & Weight: "
); // titleStyleAndWeight will be null if none of the options // was selected if (titleStyleAndWeight != null) { writer.println("
    "); for (String style : titleStyleAndWeight) { writer.print("
  • " + style + "
  • "
    ); } writer.println("
"
); } writer.println(""); } }

运行效果:

用cookie管理用户偏好:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第6张图片

CookieClassServlet类
该类的作用将Cookie类的属性写在一个Html列表中,列表中的项编号由maxRecords cookie的值决定.这个值由PreferenceServlet来设置.

package app02a.cookie;
import java.io.IOException;
import java.io.PrintWriter;
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;

@WebServlet(name = "CookieClassServlet", 
        urlPatterns = { "/cookieClass" })
public class CookieClassServlet extends HttpServlet {
    private static final long serialVersionUID = 837369L;

    private String[] methods = {
            "clone", "getComment", "getDomain",
            "getMaxAge", "getName", "getPath",
            "getSecure", "getValue", "getVersion",
            "isHttpOnly", "setComment", "setDomain",
            "setHttpOnly", "setMaxAge", "setPath",
            "setSecure", "setValue", "setVersion"
    };

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {

        Cookie[] cookies = request.getCookies();
        Cookie maxRecordsCookie = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("maxRecords")) {
                    maxRecordsCookie = cookie;
                    break;
                }
            }
        }

        int maxRecords = 5; // default
        if (maxRecordsCookie != null) {
            try {
                maxRecords = Integer.parseInt(
                        maxRecordsCookie.getValue());
            } catch (NumberFormatException e) {
                // do nothing, use maxRecords default value
            }
        }

        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("" + "Cookie Class"
                + "" 
                + PreferenceServlet.MENU
                + "
Here are some of the methods in " + "javax.servlet.http.Cookie"); writer.print("
    "); for (int i = 0; i < maxRecords; i++) { writer.print("
  • " + methods[i] + "
  • "
    ); } writer.print("
"
); writer.print("
"
); } }

运行效果:
CookieClassServlet的输出结果:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第7张图片

CookieInfoServlet类:

package app02a.cookie;
import java.io.IOException;
import java.io.PrintWriter;
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;

@WebServlet(name = "CookieInfoServlet", urlPatterns = { "/cookieInfo" })
public class CookieInfoServlet extends HttpServlet {
    private static final long serialVersionUID = 3829L;

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {

        Cookie[] cookies = request.getCookies();
        StringBuilder styles = new StringBuilder();
        styles.append(".title {");
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                String value = cookie.getValue();
                if (name.equals("titleFontSize")) {
                    styles.append("font-size:" + value + ";");
                } else if (name.equals("titleFontWeight")) {
                    styles.append("font-weight:" + value + ";");
                } else if (name.equals("titleFontStyle")) {
                    styles.append("font-style:" + value + ";");
                }
            }
        }
        styles.append("}");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("" + "Cookie Info"
                + ""
                + "" + PreferenceServlet.MENU 
                + "
" + "Session Management with Cookies:
"
); writer.print("
"); // cookies will be null if there's no cookie if (cookies == null) { writer.print("No cookie in this HTTP response."); } else { writer.println("
Cookies in this HTTP response:"
); for (Cookie cookie : cookies) { writer.println("
"
+ cookie.getName() + ":" + cookie.getValue()); } } writer.print("
"
); writer.print(""); } }

运行效果:
CookieInfoServlet的输出结果:
与Session的亲密接触&彻底掌握Java中Session Token Cookie_第8张图片

Servlet相对路径和绝对路径

一、JSP跳转到Servlet
1、相对路径,如href=”servlet/TestServlet”
*注意:如果写成”/servlet/TestServlet”会报错,因为第一个’/’表示的是【服务器根目录:http://localhost:8080/】
2、绝对路径,通过内置成员变量path实现,如href=”<%=path%>/servlet/TestServlet”。
*注意:这里的path得到的是项目根目录,如【http://localhost:8080/Servlet002_GetFormDemo】
二、Servlet跳转JSP
1、请求重定向:response.sendRedirect(request.getContextPath()+”/xxx.jsp”);这里通过request.getContextPath()方法获得项目根目录,或者通过”../xxx.jsp”取得上层路径得到
2、服务器内部转发:request.getRequestDispatcher(“../xxx.jsp”).forward(req,resp);
*小结:都可以通过../xxx.jsp得到
一、JSP跳转到Servlet
1、相对路径,如href=”servlet/TestServlet”
*注意:如果写成”/servlet/TestServlet”会报错,因为第一个’/’表示的是【服务器根目录:http://localhost:8080/】
2、绝对路径,通过内置成员变量path实现,如href=”<%=path%>/servlet/TestServlet”。
*注意:这里的path得到的是项目根目录,如【http://localhost:8080/Servlet002_GetFormDemo】
二、Servlet跳转JSP
1、请求重定向:response.sendRedirect(request.getContextPath()+”/xxx.jsp”);这里通过request.getContextPath()方法获得项目根目录,或者通过”../xxx.jsp”取得上层路径得到
2、服务器内部转发:request.getRequestDispatcher(“../xxx.jsp”).forward(req,resp);
*小结:都可以通过../xxx.jsp得到
三、web.xml配置中
< url-pattern>/servlet/HelloWorld < /url-pattern>: url-pattern处必须以 / 开头,这里/ 表示项目的根目录
在Servlet中重定向要使用获得上下文对象。getContextPath()+”/文件名。jsp”
在Servlet中如果用服务器跳转,getRequestDispatcher(“/文件.jsp”)或者getRequestDispatcher(“../文件.jsp”)采用逆向思维,获取当前目录的上一层目录
超链接的第一个斜线表示的是服务器的根目录;servlet前边打斜线,表示到根目录下打开servlet项目
利用request.getContextpath(),其代表项目的根目录
url-pattern处的斜线代表项目的根目录,必须添加
response.sendRedirect当前路径是ServletPathDirection/servlet/
服务器内部跳转,斜线代表项目根目录
Ps1:路径前缀不写相当于“./”,如“test.jsp”==“./test.jsp”
Ps2:
Ps3:更新项目名的时候注意,先检查是否在myeclipse 2015的空间包里(不是也没关系其实,最好放在这里面),然后项目——属性——MyEclipse——Project Facets——Web——Web Context-root:(服务器项目名),对应的文件夹在D:\空间名.metadata.me_tcat7\work\Catalina\localhost\Xxx——文件夹名就是服务器项目名。(注意:修改文件夹名字并不会修改服务器项目名,而修改Web Context-root才会对服务器项目名起作用)。若是类似myeclipse8.5的版本,则直接在Tomcat——webapps——项目文件夹名即是Web服务器项目名,而且修改文件夹名就会影响Web服务器项目名。
1、服务器内部跳转,这里的斜线表示项目的根目录
2.请求重定向跳转,这里的斜线表示当前目录
3.超链接和表单跳转,这里的第一个斜线表示服务器根目录,不加斜线就是当前目录
4.request.getContextPath();表示当前项目根目录

你可能感兴趣的:(JAVA,WEB)