1. 程式人生 > 其它 >四步檢查法輕鬆搞定示波器測量高速訊號

四步檢查法輕鬆搞定示波器測量高速訊號

狀態管理

現有問題

  • Http 協議是無狀態的,不能儲存每次提交的資訊。
  • 如果使用者發來一個新的請求,伺服器知道它是否與上次的請求有聯絡。
  • 對於那些需要多次提交的資料才能完成的Web 操作,比如說登入來說,就成問題了。

狀態管理概念:

​     將瀏覽器與web 伺服器之間多次互動當做一個整體來處理,並且將多次互動所涉及的資料(即狀態)儲存下來。

    簡單來說:就是 記錄客戶端和伺服器之間多次互動的狀態。

狀態管理分類:

  • 客戶端狀態管理:將狀態儲存在客戶端,代表性的是 Cookie 技術。
  • 伺服器狀態管理技術:將狀態儲存在伺服器端,代表性的是 Session 技術(伺服器傳遞 sessionID 時需要使用Cookie 的方式 和 application)

Cookie

  • Cookie 是在瀏覽器訪問 web 伺服器的某個資源時,由 web 伺服器在 HTTP 響應頭中附帶傳送給 瀏覽器的一小段資料。
  • 一旦 瀏覽器 儲存了某個Cookie ,那麼它 以後每次訪問該 web 伺服器時,都應該在 Http 請求頭中將這個 Cookie 回傳給伺服器。
  • 一個 Cookie 主要有 該資訊的名稱 (name)和 值(value)組成。

    建立 Cookie ,然後響應的時候攜帶,讓客戶端儲存,再次請求的時候,攜帶 Cookie 資訊。讓請求的 Servlet 獲取。

// 建立 Cookie
Cookie cookie = new Cookie("code",code);
// 設定 Cookie 的路徑(哪些可以訪問)
cookie.setMaxAge(-1); // 取值有三種:>0 單位:秒; =0 瀏覽器關閉;  <0 記憶體儲存,預設 -1 ;
// 將 Cookie 響應給客戶端,可以是一個也可以是多個
response.addCookie(cookie);  // 新增到 response 物件中,響應時傳送給客戶端
// 通過 request 物件獲取所有的 cookie
        Cookie[] cookies = req.getCookies();

        // 通過 迴圈遍歷陣列Cookie
/*        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+":"+cookie.getValue());

        }*/


        // 但是有可能傳入 的cookie 是一個空,那麼如何遍歷呢,或者說如何避免空指標異常呢?
        // 簡單:加個判斷就好

        if (cookies != null){
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + ":" + cookie.getValue());
            }
        }

只需要保證 Cookie 的名稱和路徑是一致的。這時會認為修改操作。

// 建立 Cookie
Cookie cookie = new Cookie("code","code");
cookie.setPath("/webs");  // 必須是一致的,不一樣就新建了個 cookie
cookie.setMaxAge(-1);
response.addCookie(cookie);

注意】:如果改變 name 和有效路徑會新建 cookie;而改變 cookie 的 value 、有效期,會覆蓋原有的 cookie。

很簡單。。。

4、Cookie 的編碼與解碼

​     Cookie 預設不支援中文,只能包含 ASCII 字元,所以 cookie 需要對 Unicode 字元進行編碼,否則會出現亂碼。

  • 編碼使用 java.net.URLEncoder 類中的,encode(String str,String encoding) 方法。(對指定的字串,用指定的編碼格式編碼)
  • 解碼使用 java.net.URLDecoder 類的decode(String str,String encoding)方法。

建立帶中文的 cookie(編碼)

Cookie cookie = new Cookie("姓名", "張三");  // cookie 不支援
cookie.setPath("/webproject/get");
cookie.setMaxAge(60 * 60);
resp.addCookie(cookie);

需要設定 編碼格式

Cookie cookie = new Cookie(
        URLEncoder.encode("姓名","utf-8"),
        URLEncoder.encode("張三","utf-8"));
cookie.setPath("/webproject/get");
cookie.setMaxAge(60 * 60);
resp.addCookie(cookie);

    同時也需要設定,解碼方式:(不然出現的是%E5%A7%93%E5%90%8D:%E5%BC%A0%E4%B8%89)

if (cookies != null) {
    for (Cookie cookie : cookies) {
        System.out.println(
                URLDecoder.decode(cookie.getName(), "utf-8")
                        + ":" + URLDecoder.decode(cookie.getValue(), "utf-8"));
    }
}

總結

優點:

  1. 可配置到期規則
  2. 簡單性:Cookie 是一種基於文字的輕量結構,包含簡單的鍵值對。
  3. 資料永續性:cookie 預設在過期之前是可以一直存在客戶端瀏覽器上。

缺點:

  1. 大小收到限制:大多數瀏覽器對cookie 的大小有 4k,到8k 位元組的限制。
  2. 有些使用者配置禁用,就會限制 cookie 功能
  3. 潛在安全風險,cookie 可能會被篡改,會對安全性造成風險或者導致依賴於cookie 的應用程式失敗。

Session

概述

  • Session 用於記錄使用者的狀態,Session 指的是在一段時間內,單個客戶端與 web 伺服器的一連串相關的互動過程。
  • 在一個 Session 中,客戶可能會多次請求訪問同一個資源,也有可能請求訪問各種不同的伺服器資源。

Session 原理

  • 伺服器會為每一次會話分配一個 Session 物件。
  • 同一個瀏覽器發起的多次請求,同屬於一次會話。
  • 首次使用到 Session 時,伺服器會自動建立 Session,並建立 Cookie 儲存 SessionID ,並且回傳給客戶端。

注意:Session 是服務端建立的。

首次請求:在響應中設定 cookie

未關閉瀏覽器,再次請求:請求中攜帶 Session

Session 的使用

session 是伺服器的一個 map 集合。

  • Session 作用域:擁有儲存資料的空間,作用範圍是一次會話有效。
    • 一次會話:是使用統一瀏覽器傳送的多次請求,一旦瀏覽器關閉,則會話結束。
    • 可以將資料存入 Session 中,在一次會話的任意位置進行獲取。
    • 可傳遞任何資料(基本資料型別,物件,集合,陣列)

瀏覽器在沒有關閉的情況下,訪問同一個資源,拿到的是同一個 SessionID

1、獲取 Session 物件

// 獲取 Session 物件
HttpSession session = request.getSession();
// 唯一標識
System.out.println("session :"+session.getId());

2、Session 儲存資料

setAttribute(屬性名,Object)儲存資料到 session 中

session.setAttribute("key",value); // 以鍵值對形式儲存在 session 作用域中

3、Session 獲取資料

getAttribute(屬性名); 獲取 session 中的資料

session.setAttribute("key");  // 通過String 型別的key 訪問Object 型別的 value

4、Session 移除資料

removeAttribute(屬性名); 從session 中移除資料

removeAttribute("key");  // 通過鍵移除session 作用域中的值

Session 和 Request 應用區別

區別

  • request 是一次請求有效,請求改變,則 request 改變。

  • session 是一次會話有效,只要瀏覽器不關閉,那麼 Session 是不會改變的。

例項

@WebServlet(value = "/ss")
public class SessionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 獲取 Session 物件
        HttpSession session = req.getSession();
        System.out.println("session :"+session.getId());
        session.setAttribute("name", "Asia");

        // 用 request 域傳遞資料
        req.setAttribute("password", "123");
        resp.sendRedirect("/webproject/getvalue");


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }



}

獲取值的 Servlet

@WebServlet(value = "/getvalue")
public class GetValue extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 獲取 Session 物件
        HttpSession session = req.getSession();

        // 獲取 Session 資料
        String name = (String) session.getAttribute("name");

        System.out.println("name:" + name);

        // 獲取 request 域中的 資料,這裡因為採取的是定向的方式,所以是獲取不到的
        String password = (String) req.getAttribute("password");
        System.out.println(password);


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

結果:(先訪問 /ss,然後重定向到 /getvalue )以證明request 和 session 的區別。

session :F41D767BD803DDEBDE5E9AB2F6C9B819
name:Asia
null

Session 的生命週期

  • 開始:第一次使用到Session 的請求產生,則建立 Session
  • 結束:
    • 瀏覽器關閉,失效
    • Session 超時,失效
      • session.setMaxInactiveInterval(60 * 60);
    • 手動銷燬,失效
      • session.invalidate(); // 登入退出、登出
@WebServlet(value = "/gets")
public class getSessionLife extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 獲取 Session 物件
        HttpSession session = req.getSession();

        // 設定 session 有效時間
      //  session.setMaxInactiveInterval(10);  // 以秒為單位


        System.out.println("第一次獲取 sessionID:" + session.getId());

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


}
@WebServlet(value = "/secget")
public class SecondGetLife extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 獲取 Session 物件
        HttpSession session = req.getSession();


        System.out.println("第二次獲取 sessionID :" + session.getId());  // 因為我是在一次會話請求的這個 Servlet,同時我還設定了 10 秒超時,所以 10 秒後,超時了,第二次獲取到的 session  和第一次是不一樣的。


        // 手動銷燬失效    手動銷燬後,在此獲取到 session 是 新session
        session.invalidate();

        /*
        *   第一次獲取 sessionID:969168F6CD8A47168209EB8D13E438E0
            第二次獲取 sessionID :969168F6CD8A47168209EB8D13E438E0
            再次訪問第一次獲取的 servlet: sessionID:99C5CAFCE6BD0287F7CBC8B6621A92AB
        * */
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


}

瀏覽器禁用 cookie 的後果

​     現在大家都很喜歡將 cookie 全部禁用了,防止在看學習資料的時候,被別人發現。

​     伺服器預設情況下,會使用 cookie 的方式將 sessionID 傳送給瀏覽器,我們禁止了,sessionID 就不會儲存,那我們多次獲取到的 sessionID 就是不同了,這就屬於一次會話,多個 sessionID ,這當然是不行的了。

解決方法:URL 重寫

​     瀏覽器在訪問伺服器上的某個地址時,不再使用原來的那個地址,而是使用經過改寫的地址(即在原來的地址後面加上了 sessionID

實現:

response.encodeRedirectURL(String url) 生成重寫的 URL

@WebServlet(value = "/gets")
public class getSessionLife extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 獲取 Session 物件
        HttpSession session = req.getSession();

        // 設定 session 有效時間
      //  session.setMaxInactiveInterval(10);  // 以秒為單位


        // 生成 重寫 URL
        String newURI = resp.encodeRedirectURL("/webproject/secget");


        System.out.println("第一次獲取 sessionID:" + session.getId());

        System.out.println(newURI);
        resp.sendRedirect(newURI);


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


}
/*

第一次獲取 sessionID:62DFA4B0640D24471A0B00FAB4D3C948
/webproject/secget;jsessionid=62DFA4B0640D24471A0B00FAB4D3C948
第二次獲取 sessionID :62DFA4B0640D24471A0B00FAB4D3C948


*/

​ 做到了 即使客戶端禁用了 cookie ,我仍舊可以傳 serssionID 來保證,兩次請求都屬於同一個 session 會話。

Session 應用場景

1、儲存使用者登入資訊

寫一個管理員的實體類,mapper,service,controller,挨個寫,在 controller 中做判斷。

if (mgr != null){
    // 登入成功

    // 將管理員資訊存在 Session 裡面 (這樣我可以在任何位置,訪問管理員的資訊)
    // 有效期是一次會話
    HttpSession session = req.getSession();
    session.setAttribute("mgr", mgr);
    // 跳轉,目標,方式(通過重定向的方式,跳轉到 showallController)
    resp.sendRedirect("/webproject/showAllController");
} else {
    // 登入失敗
    resp.sendRedirect("/webproject/loginMgr.html");
}

​     在 showAllController 中,也要做判斷,如果登入過的話,可以直接訪問,沒有登入,重定向到登入頁面,這樣做是為了防止,使用者直接訪問 showAllController.

// 通過 HttpSession 完成許可權的控制
HttpSession session = req.getSession();
Manager mgr = (Manager) session.getAttribute("mgr");

// 如果登入了,執行業務,沒有登入,則登入失敗,跳轉到登入頁面。 
if (mgr != null){

    System.out.println("進入 /showAllController  doGet");


    AdminService adminService = new AdminServiceImpl();
    List<User> userList = adminService.showAllUser();

    // request 作用域存資料
    req.setAttribute("user", userList);
    // 通過轉發,跳轉到顯示結果的 servlet
    req.getRequestDispatcher("/showAlljsp").forward(req, resp);
} else{
    // 登入失敗,轉到登入頁面登入
    resp.sendRedirect("/webproject/loginMgr.html");
}

​     同時因為是一次會話,所以我們可以在任意位置,獲取,session 的資訊,session 存的是管理員資訊,所以可以做到,顯示 welcome ,管理員! 這樣的效果。

2、儲存驗證碼

  • 匯入 validateCode.jar
  • 建立生成驗證碼 的 servlet

思路:

​     建立生成驗證碼的 servlet ,建立驗證碼圖片和響應給客戶端,這時候,在 logMgr.html 頁面已經有驗證碼了,接下來,我們就需要在 loginMrgController 裡面首先收參,然後判斷驗證碼,是否正確,接著再進行後續業務的比較。

​ 【注意】:建立 驗證碼 生成的 servlet 的時候,將 驗證碼獲取,然後存在 session 裡面,然後在 loginMgrController 裡面,獲取session 中存的 驗證碼,然後進行比較。

login.html

<form action="/webproject/loginMgr" method="post">
    使用者名稱:<input type="text" name="username"><br>
    密 碼:<input type="password" name="password"><br>
    驗證碼:<input type="text" name="inputVcode"/><img src="/webproject/createCode"><br> <!--這裡已經對createCode 做了一次請求,響應。再次點選登入,那就是兩次請求,所以,不能用request 存驗證碼資料-->
    <input type="submit" value="登入">


</form>

驗證碼生成 servlet

@WebServlet(value = "/createCode")
public class CreateCodeController extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 1. 建立驗證碼圖片

        ValidateCode vc = new ValidateCode(150, 30, 5, 10);

        // 2. 驗證碼圖片響應給客戶端
        vc.write(resp.getOutputStream());

        // 3. 建立session  儲存 驗證碼
        // 獲取驗證碼生成 的5個字元  在 ValidateCode中 有個 getcode方法
        String codes = vc.getCode();

        HttpSession session = req.getSession();
        session.setAttribute("code",codes);

    }

loginMgrController servlet

@WebServlet(value = "/loginMgr")
public class LoginMgrController extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 1. 處理亂碼

        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        // 2. 收參
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String inputVcode = req.getParameter("inputVcode");


        // 首先判斷,驗證碼,是否正確,然後,接著判斷業務邏輯

        // 從session 中 獲取隨機生成的驗證碼,然後我們在呼叫業務方法,進行後續的比較
        String code = (String) req.getSession().getAttribute("code");
        if (inputVcode != null && inputVcode.equalsIgnoreCase(code)) {

            // 3. 呼叫業務方法
            ManagerServiceImpl managerService = new ManagerServiceImpl();
            Manager mgr = managerService.login(username, password);


            // 4. 處理結果,流程跳轉

            if (mgr != null) {
                // 登入成功

                // 將管理員資訊存在 Session 裡面 (這樣我可以在任何位置,訪問管理員的資訊)
                // 有效期是一次會話
                HttpSession session = req.getSession();
                session.setAttribute("mgr", mgr);
                // 跳轉,目標,方式(通過重定向的方式,跳轉到 showallController)
                resp.sendRedirect("/webproject/showAllController");
            } else {
                // 登入失敗
                resp.sendRedirect("/webproject/loginMgr.html");
            }
        }else {
            resp.sendRedirect("/webproject/loginMgr.html");
        }

    }