encodeURL()



HttpSession 預設使用 Cookie 來儲存 Session ID,如果使用者關掉瀏覽器接受 Cookie 的功能,那就無法使用 Cookie 在瀏覽器儲存 Session ID。如果在使用者禁用 Cookie 的情況下,仍打算運用 HttpSession 來進行會話管理,那麼可以搭配〈URL 重寫〉的方式,向瀏覽器回應一段超鏈結,超鏈結 URL 後附加 Session ID,當使用者按下超鏈結,將 Session ID 以 GET 請求發送給 Web 應用程式。

如果你要使用 URL 重寫的方式來發送 Session ID,可以使用 HttpServletResponseencodeURL() 協助產生所需的 URL 重寫。當容器嘗試取得 HttpSession 實例時,若可以從 HTTP 請求中取得帶有 Session ID 的 Cookie 時,encodeURL() 會將設定給它的 URL 原原本本地輸出。如果容器嘗試取得 HttpSession 實例時,無法從 HTTP 請求中取得帶有 Session ID 的 Cooki e時(通常是瀏覽器禁用 Cookie 的情況), encodeURL() 會自動產生帶有 Session ID 的 URL 重寫。例如:

package cc.openhome;

import java.io.*;
import java.util.Optional;

import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebServlet("/counter")
public class Counter extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html; charset=UTF-8");

        Integer count = Optional.ofNullable(request.getSession().getAttribute("count"))
                                .map(attr -> (Integer) attr + 1)
                                .orElse(0);
        request.getSession().setAttribute("count", count);

        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<meta charset='UTF-8'>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Servlet Count " + count + "</h1>");
        out.printf("<a href='%s'>遞增</a>%n", response.encodeURL("counter"));
        out.println("</body>");
        out.println("</html>");
    }
}

這個程式會顯示一個超鏈結,如果你按下超鏈結,則會造訪同一個 URL,在關閉瀏覽器前,每按下超鏈結都會遞增數字。如果你的瀏覽器沒有禁用 Cookie,則 encodeURL() 產生的超鏈結網址就是原本的 counter,如果你的瀏覽器禁用 Cookie,則在超鏈結網址上編上 Session ID。例如:

<a href="counter;jsessionid=42B0F705E7C4314988DDD90480710FFC">遞增</a>

如果你不使用 encodeURL() 來產生超鏈結的 URL,在瀏覽器禁用 Cookie 的情況下,這個程式將會失效,也就是你重複按遞增鏈結,計數也不會遞增。

如果你有執行 encodeURL(),在瀏覽器第一次請求網站時,容器並不知道瀏覽器是否禁用 Cookie,所以容器的作法是 Cookie(發送 set-cookie 標頭)與 URL 重寫都作,因此若你的 Servlet 有以下陳述,無論瀏覽器有無禁用 Cookie,第一次請求時,都會顯示編上Session ID的URL:

request.getSession();
out.println(response.encodeURL("index.jsp"));

當再次請求時,如果瀏覽器沒有禁用 Cookie,則容器可以從 Cookie(從 cookie 標頭)中取得 Session ID,則 encodeURL() 就只會輸出 index.jsp。如果瀏覽器禁用 Cookie,由於無法從 Cookie 中得 Session ID,此時 encodeURL() 就會在 URL 編上 Session ID。

總而言之,當容器嘗試取得 HttpSession 物件時,無法從 Cookie 中取得 Session ID,使用 encodeURL() 就會為你產生有 Session ID 的 URL,以便於下次點選超鏈結時再度發送 Session ID。

另一個 HttpServletResponse 上的 encodeRedirectURL() 方法,則可以在你要求瀏覽器重新導向時,在 URL 編上 Session ID。

雖然說,使用者會為了隱私權等原因而禁用 Cookie,然而,就今日來看,在 URL 上直接出現 Session ID,反而會有安全上的隱憂,這會使得有心人士在指定特定 Session ID 變得容易(Session Fixation),或者在從目前網址鏈結至另一網址時,因為 HTTP Referer 標頭而洩漏了 Session ID。