cookie和session(请求转发和请求重定向的区别、cookie 和 session的区别、session的生命周期)

cookie

cookie 本意是小甜点, 在web开发中是用来维系状态的一种技术,可以用它实现记住用户名、密码这些参数的操作,就如我们每次登陆页面,跳转到其他页面时我们还是登陆的状态,这就用到了cookie记住用户名密码这一项功能(也就是服务器要向浏览器返回cookie),它的缺点是有安全风险,因为信息是存储在浏览器端的。

创建cookie

  • 服务器要向浏览器返回cookie

        Cookie cookie的名字 = new Cookie(名, 值);  创建cookie对象
        response.addCookie(cookie的名字);      把cookie加入到相应中,返回给浏览器

  • 浏览器再发送请求时,会把这些cookie值重新发送给服务器

        Cookie[] cookies = request.getCookies(); 返回的是一个cookie数组,里面包含各种cookie信息

cookie 的属性

  • maxAge 用来设置 cookie 的寿命

        默认不设置(-1)表示浏览器关闭寿命就到期 
        指定一个正整数(单位秒),指定cookie存活多久
        设置为 0,表示由服务器端删除该cookie

  • httpOnly 用来设置是否禁止 js 代码访问 cookie(true 禁止,false 可以)

代码演示

1、添加cookie、设置寿命、

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 java.io.IOException;

@WebServlet(urlPatterns = "/cookieResp")
public class CookieRespServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Cookie up = new Cookie("up", "zhangsan:123");
        up.setMaxAge(3600); // 一小时
        up.setHttpOnly(true); // 不允许 js 代码操作这个 cookie , 否则,可以使用 document.cookie 访问到cookie

        resp.addCookie(up);//添加cookie
        resp.getWriter().print("cookie added...");//浏览器输出cookie added...
    }
}

结果                                                      

 2、遍历服务器输出cookie内容

@WebServlet("/cookieReq")
public class CookieReqServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Cookie[] cookies = req.getCookies();//获得cookie
        //遍历cookie
        for (Cookie cookie : cookies) {
            //cookie.getName() 获得cookie名,cookie.getValue() 获得cookie值
            System.out.println(cookie.getName() + " " + cookie.getValue());
        }
    }
}

结果

可以看到我存进去的cookie up zhangsan:123,其他的cookie是浏览器传过来的。

session

session也是用来维系状态的一种技术,功能和cookie差不多,也是可以用来存储用户名、密码,但是它把这些状态信息存储在服务器端,安全性要比 cookie 高很多,可以用session实现在一个浏览器保持登录状态,不需要重复登录,也可以说是自动登录。

session的应用

  • 存储信息

        HttpSession session = request.getSession();   // 拿到 session 对象

        session.setAttribute("名", 值);  // 存储信息

  •   获取信息

        HttpSession session = request.getSession();  // 拿到 session 对象

        session.getAttribute("名"); // 返回上一次存储的值

  •   删除信息

          session.removeAttribute("名"); // 移除指定的session,返回被移除的值

          session.invalidate(); // 让session失效(全部清空)

session默认生命周期

第一次调用 request.getSession() 创建 session对象,如果隔了 30 分钟没有向服务器发送请求,session 会自动失效。

如果要改变失效时间,可以在 web.xml中:
    <session-config>
         <session-timeout>过期时间,如:30(单位是分钟)</session-timeout>
    </session-config>

跟浏览器的关系:一个浏览器对应服务器端的一个 session 对象,他们存储的信息互不干扰

cookie 和 session的区别

  1.  安全性上, session的安全性高,cookie的信息存在浏览器端所以不安全
  2.  存储的类型, session 存储的类型是 Object, cookie 只能存字符串(并且需要进行编码处理)
  3.  存储大小, session 理论上没有限制(但不建议存储太多内容), cookie 的限制:每个cookie不能超过4k,每个网站cookie个数也有限制的
  4.  失效时间, session 两次请求间隔30分钟, cookie 默认关闭浏览器失效,还可以通过 maxAge 调整的更长

请求重定向与请求转发的区别

  • 请求转发

        request.getRequestDispatcher("跳转路径").forward(request, response);

  • 请求重定向

        response.sendRedirect("跳转路径");

        如:重定向到 index.jsp 如果没有加/ 是相对路径
        http://localhost:8080/user/index.jsp

        如果加了 / ,就是相对于主机名和端口号 也就是直接在路径前加 http://localhost:8080

        结论:如果路径中有多层目录,建议以 / 写一个完整路径

 二者的区别:
        请求转发的地址不会改动,始终是刚开始的地址, 请求重定向在跳转后,地址栏会变为目标地址
        请求转发是一次请求,跳转操作在服务器内部发生;请求重定向是两次请求,跳转操作是在浏览器与服务器之间发生
        请求转发可以使用 request.setAttribute 进行值的传递;请求重定向需要使用 session.setAttribute 进行值的传递

作用域(scope)对象 

  • request作用范围限于一次请求

        request.setAttribute(key, value); 
        request.getAttribute(key)    ${key}
        request.removeAttribute(key);

  • session作用范围同一个浏览器的多次请求之间(一次会话)

        session.setAttribute(key, value); 
        session.getAttribute(key)    ${key}
        session.removeAttribute(key);

  • 页面作用域,作用范围限于当前页面

         page

  • 应用程序作用域,作用于整个应用程序

        application

  • 作用于顺序

        page < request < sesssion < application 

        ${ key } 这类型的EL会从小的作用域向大的作用域依次查找,直到找到为止

        也可以利用前缀精确地找某个作用域:
                                   pageScope page 作用域
                                   requestScope 请求作用域
                                   sessionScope  会话作用域
                                   applicationScope 应用程序作用域

会话跟踪的原理

 request.getSession(); // 首次请求,在服务器端创建出 session 对象, session会有一个id值(唯一的)
 第一次返回响应时,由tomcat 返回一个特殊cookie (jsessionid=session的id值) 返回给浏览器
 后续的请求,浏览器就根据这个 jsessionid的值找到服务器端的session对象

代码演示

1、作用域显示( page 、request 、sesssion 、application )

scope.jsp上显示

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        // 向 page 作用域
        pageContext.setAttribute("key1", "value1");
        // 向 request 作用域
        request.setAttribute("key2", "value2");
        // 向 session 作用域
        session.setAttribute("key3", "value3");
        // 向 application 作用域
        application.setAttribute("key4", "value4");
    %>
    <%--显示key对应的value值--%>
    <h5>page: ${key1}</h5>
    <h5>request: ${key2}</h5>
    <h5>session: ${key3}</h5>
    <h5>application: ${key4}</h5>
</body>
</html>

结果

s2.jsp上显示,也就是在另一个页面上显示

scope.jsp

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        // 向 page 作用域
        pageContext.setAttribute("key1", "value1");
        // 向 request 作用域
        request.setAttribute("key2", "value2");
        // 向 session 作用域
        session.setAttribute("key3", "value3");
        // 向 application 作用域
        application.setAttribute("key4", "value4");
        //发送请求到s2.jsp,这个和下面一个都可以 request.getRequestDispatcher("s2.jsp").forward(request, response);
        response.sendRedirect("s2.jsp");
    %>
</body>
</html>

s2.jsp 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--显示key对应的value值--%>
<h5>page: ${key1}</h5>
<h5>request: ${key2}</h5>
<h5>session: ${key3}</h5>
<h5>application: ${key4}</h5>
</body>
</html>

结果

从以上对比可以看出page的作用范围限于当前页面,因此它的值只出现在当前页面上;request作用范围限于一次请求,因此它的值只出现在当前页面上;session作用范围同一个浏览器的多次请求之间,因此在另一个页面上也可以显示它的值;aplication应用程序作用于整个应用程序,因此此程序范围内都可以显示该值。

2、登录与退出,如果不登录就进不到欢迎界面

存储用户信息代码,要序列化,用户密码是敏感信息,因此不许需要在toString中写出

import java.io.Serializable;

public class User implements Serializable {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

连接数据库代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JdbcUtils {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/school", "root", "root");
        return conn;
    }

}

查询用户密码和用户名的代码

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {

    /**
     * 根据用户名查询用户对象
     * @param username 用户名
     * @return 查询到了,返回用户对象,如果用户不存在返回 null
     */
    public User findByUsername(String username) {
        try(Connection conn = JdbcUtils.getConnection()) {
            try(PreparedStatement stmt = conn.prepareStatement("select * from user where uname=?")) {
                stmt.setString(1, username);
                ResultSet rs = stmt.executeQuery();
                if(rs.next()) {
                    // 查询到了该用户的用户名和密码,并存入user中,然后返回该user
                    User user = new User();
                    user.setUsername(rs.getString("uname"));
                    user.setPassword(rs.getString("password"));
                    return user;
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
}

登录界面(login.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
    <%--提交给/login,也就是LoginServlet,验证是否有该用户--%>
    <form action="login" method="post">
        <p>用户名:<input type="text" name="username"></p>
        <p>密码:<input type="password" name="password" ></p>
        <p><input type="submit" value="登录"></p>
    </form>
    <h4 style="color:red;">${error}</h4>
</body>
</html>

验证用户名与密码(LoginServlet)

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {

    private UserDao userDao = new UserDao();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        User user = userDao.findByUsername(username);
        // 数据库没有此用户
        if (user == null) {
            req.setAttribute("error", "用户不存在");
            req.getRequestDispatcher("login.jsp").forward(req, resp);
            return;
        }

        // 如果数据库的密码 不等于 输入密码
        if (!user.getPassword().equals(password)) {
            req.setAttribute("error", "密码错误");
            req.getRequestDispatcher("login.jsp").forward(req, resp);
            return;
        }

        // 通过验证
        // 将登录标记存入 session 作用域
        req.getSession().setAttribute("username", username);

        req.getRequestDispatcher("welcome.jsp").forward(req, resp);

    }
}

登录成功界面,如果没有登录不能直接打开该界面(welcome.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    // 获取 session 中的 username 变量
    Object username = session.getAttribute("username");
    // 没有经过验证
    if(username == null) {
        /*
        请求转发的方式,跳转到login.jsp
        request.setAttribute("error", "您尚未登录");
        request.getRequestDispatcher("login.jsp").forward(request, response);
        */
        // 请求重定向方式
        session.setAttribute("error", "您尚未登录");
        response.sendRedirect("login.jsp");
        return;
    }
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <!-- EL 可以从session作用域中获取信息-->
    登录成功!!!  欢迎 ${username} 来到本网站
    <!-- 退出跳转到/logout处理-->
    <a href="logout">注销(安全退出)</a>
</body>
</html>

退出功能代码(LogoutServlet)

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet(urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        // 让 session 失效
        session.invalidate();
        // 跳转至登录页面
        req.getRequestDispatcher("login.jsp").forward(req, resp);
    }
}

结果

登录界面

登陆成功

 登录失败(显示密码不存在或用户名不存在)

总结

虽然这两种都是维系状态的一种技术,但是cookie有安全风险,因为信息是存储在浏览器端的,而session把这些状态信息存储在服务器端,因此安全性要比 cookie 高很多,并且使用session可以实现自动登录,这样可以方便跳转页面时,不需要在登录页面                                        。

猜你喜欢

转载自blog.csdn.net/grey_mouse/article/details/86572313
今日推荐