與Session的親密接觸&徹底掌握Java中Session Token Cookie

Session管理


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<String> londonAttractions;
    private List<String> parisAttractions;

    @Override
    public void init() throws ServletException {
        londonAttractions = new ArrayList<String>(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<String>(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("<html><head>" +
                "<title>Top 10 Tourist Attractions</title>" +
                "</head><body>" + ""
                "Please select a city:" +
                "<br/><a href='?city=london'>London</a>" +
                "<br/><a href='?city=paris'>Paris</a>" +
                "</body></html>");
    }

    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<String> 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("<html><head>" +
                "<title>Top 10 Tourist Attractions</title>" +
                "</head><body>");
        writer.println("<a href='top10'>Select City</a> ");
        writer.println("<hr/>Page " + page + "<hr/>");

        int start = page * 5 - 5;
        for (int i = start; i < start + 5; i++) {
            writer.println(attractions.get(i) + "<br/>");
        }
        writer.print("<hr style='color:blue'/>" +
                "<a href='?city=" + city + 
                "&page=1'>Page 1</a>");
        writer.println("&nbsp; <a href='?city=" + city + 
                "&page=2'>Page 2</a>");
        writer.println("</body></html>");
    }
}

最值得關注的是a標籤的href屬性值,它包含一個問號,接着是token:city=london或着city=paris.任何相對的URL(沒有協議部分的URL)都會被當作是相對於當前頁面的URL,如果點擊頁面的某一個超鏈接,那麼:

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

或者

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

就會被髮送到服務器.
運行效果:
初始頁:
這裏寫圖片描述
倫敦前十大旅遊地中的第一頁:
這裏寫圖片描述
倫敦十大旅遊地的第二頁:
這裏寫圖片描述

隱藏域

利用隱藏域來保存狀態,與採用網址重寫技術類似.只是它不把值放到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<Customer> customers = new ArrayList<Customer>();

    @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("<html><head><title>Customers</title></head>"
                + "<body><h2>Customers </h2>");
        writer.println("<ul>");
        for (Customer customer : customers) {
            writer.println("<li>" + customer.getName() 
                   + "(" + customer.getCity() + ") (" 
                   + "<a href='editCustomer?id=" + customer.getId()
                   + "'>edit</a>)");
        }
        writer.println("</ul>");
        writer.println("</body></html>");

    }

    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("<html><head>"
                    + "<title>Edit Customer</title></head>"
                    + "<body><h2>Edit Customer</h2>"
                    + "<form method='post' "
                    + "action='updateCustomer'>");
            writer.println("<input type='hidden' name='id' value='" 
                    + customerId + "'/>");
            writer.println("<table>");
            writer.println("<tr><td>Name:</td><td>" 
                    + "<input name='name' value='" + 
                    customer.getName().replaceAll("'", "&#39;") 
                    + "'/></td></tr>");
            writer.println("<tr><td>City:</td><td>" 
                    + "<input name='city' value='" + 
                    customer.getCity().replaceAll("'", "&#39;") 
                    + "'/></td></tr>");
            writer.println("<tr>"
                    + "<td colspan='2' style='text-align:right'>" 
                    + "<input type='submit' value='Update'/></td>" 
                    + "</tr>");
            writer.println("<tr><td colspan='2'>"
                    + "<a href='customer'>Customer List</a>" 
                    + "</td></tr>");
            writer.println("</table>");
            writer.println("</form></body>");
        } 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);
    }
}

運行效果:
客戶列表:
這裏寫圖片描述
Edit Customer表單:
這裏寫圖片描述
注意到表單中的隱藏域沒有?它包含了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 = 
            "<div style='background:#e8e8e8;"
            + "padding:15px'>"
            + "<a href='cookieClass'>Cookie Class</a>&nbsp;&nbsp;"
            + "<a href='cookieInfo'>Cookie Info</a>&nbsp;&nbsp;"
            + "<a href='preference'>Preference</a>" + "</div>";

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head>" + "<title>Preference</title>"
                + "<style>table {" + "font-size:small;"
                + "background:NavajoWhite }</style>"
                + "</head><body>"
                + MENU
                + "Please select the values below:"
                + "<form method='post'>"
                + "<table>"
                + "<tr><td>Title Font Size: </td>"
                + "<td><select name='titleFontSize'>"
                + "<option>large</option>"
                + "<option>x-large</option>"
                + "<option>xx-large</option>"
                + "</select></td>"
                + "</tr>"
                + "<tr><td>Title Style & Weight: </td>"
                +"<td><select name='titleStyleAndWeight' multiple>"
                + "<option>italic</option>"
                + "<option>bold</option>"
                + "</select></td>"
                + "</tr>"
                + "<tr><td>Max. Records in Table: </td>"
                + "<td><select name='maxRecords'>"
                + "<option>5</option>"
                + "<option>10</option>"
                + "</select></td>"
                + "</tr>"
                + "<tr><td rowspan='2'>"
                + "<input type='submit' value='Set'/></td>"
                + "</tr>"
                + "</table>" + "</form>" + "</body></html>");

    }

    @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("<html><head>" + "<title>Preference</title>"
                + "</head><body>" + MENU
                + "Your preference has been set."
                + "<br/><br/>Max. Records in Table: " + maxRecords
                + "<br/>Title Font Size: " + titleFontSize
                + "<br/>Title Font Style & Weight: ");

        // titleStyleAndWeight will be null if none of the options
        // was selected
        if (titleStyleAndWeight != null) {
            writer.println("<ul>");
            for (String style : titleStyleAndWeight) {
                writer.print("<li>" + style + "</li>");
            }
            writer.println("</ul>");
        }
        writer.println("</body></html>");
    }
}

運行效果:

用cookie管理用戶偏好:
這裏寫圖片描述

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("<html><head>" + "<title>Cookie Class</title>"
                + "</head><body>" 
                + PreferenceServlet.MENU
                + "<div>Here are some of the methods in " +
                        "javax.servlet.http.Cookie");
        writer.print("<ul>");

        for (int i = 0; i < maxRecords; i++) {
            writer.print("<li>" + methods[i] + "</li>");
        }
        writer.print("</ul>");
        writer.print("</div></body></html>");
    }
}

運行效果:
CookieClassServlet的輸出結果:
這裏寫圖片描述

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("<html><head>" + "<title>Cookie Info</title>"
                + "<style>" + styles.toString() + "</style>"
                + "</head><body>" + PreferenceServlet.MENU 
                + "<div class='title'>"
                + "Session Management with Cookies:</div>");
        writer.print("<div>");

        // cookies will be null if there's no cookie
        if (cookies == null) {
            writer.print("No cookie in this HTTP response.");
        } else {
            writer.println("<br/>Cookies in this HTTP response:");
            for (Cookie cookie : cookies) {
                writer.println("<br/>" + cookie.getName() + ":"
                        + cookie.getValue());
            }
        }
        writer.print("</div>");
        writer.print("</body></html>");
    }
}

運行效果:
CookieInfoServlet的輸出結果:
這裏寫圖片描述

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();表示當前項目根目錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章