由于HTTP协议是无持续状态的协议,所以在 HTTP协议 实现会话状态保持是一个挑战,目前主要的做法是采用 Cookies 和 Session 技术解决。
HTTP协议是无持续状态的, 数据发送完毕,连接立即释放。 这种工作方式可以使用服务器的资源充分复用,可以为更多客户端服务。简称:无状态协议
无状态协议:由于服务器服务结束以后,释放了连接,不再与客户端保持状态, 无法记住客户端, 如: 客户是否登录过。
目录
1、Cookies 技术
Cookies 技术,是在HTTP协议基础之上实现状态保持技术,其工作原理与会员卡类似:
- 在客户端第一次来访时候,发会员卡(cookie)。
- 服务端可以将信息记录到卡上。
- 客户端在每次来访时候, 都要带上会员卡用于识别身份。
- 状态信息记录在 会员卡 上。
Cookies 原理:
- 在浏览器第一次来访时候,发Cookies。
- 服务器可以将信息记录到Cookies上。
- 浏览器在每次来访时候, 都要带上Cookies用于识别身份。
- 状态信息记录在 Cookies 上。
Cookie 饼干,当年 Netscape 公司设计了一个无厘头名字。
Cookies的使用:
- Cookies 现在作为HTTP协议扩展功能,被浏览器广泛支持。
- Cookies 利用 HTTP 协议 消息头传送数据。
- Cookies 是一个HTTP协议扩展标准,其协议头是严格规定的。
- Java Servlet API 提供Cookies API,只需要使用这些API就能自动下发Cookies,自动获取上传Cookies。
Cookie中的特殊字符和中文
由于Cookie是在Http协议头部传送的, Http协议头必须是 ISO8859-1 编码,并且不能使用特殊字符和中文。
-
利用 Escape 编码进行解决,是HTTP协议标准。
-
数据加入Cookie之前进行编码即可。
-
Java 提供了编码API
URLEncoder.encode(“您好Cookie”, “UTF-8”)
- 解码API
URLDecoder.decode(cookie.getValue(),“utf-8”)
Cookie的域名和路径
- 浏览器在保存Cookie时,每个网站保存一组对应Cookie,在发送时候发送到对应的网站。 根据域名区分不同的网站
- 同一个网站的Cookie按照路径分级存储,回传时候按照路径级别回传:
- 目录中可以收到当前目录的Cookie和上级目录的Cookie
- 下发Cookie时候,默认情况下按照当前目录层次保存Cookie,可以利用setPath属性设置Cookie的保存路径。
注意:
服务器下发的Cookie保存在浏览器的内存中。
不同浏览器属于不同的客户端,不同的客户端拿不到其他客户端下发的Cookie,一旦浏览器关闭就会默认清空Cookie,在不关闭浏览器的情况下,Cookie默认存活30min。
Cookie可以设置存活时间,一旦设置了存活时间Cookie就不再保存在浏览器内存中,而是保存在本地硬盘中,Cookie不会再因为浏览器的关闭而清空,可以利用Cookie来保存用户名,但不能用来保存密码!!!
cookie.setMaxAge(60);//以秒为单位
设置Cookie
public class SetCookie extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response){
try {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Cookie cookie1= null;
try {
cookie1 = new Cookie("cookie1", URLEncoder.encode(" 你好cookie","utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Cookie cookie2=new Cookie("cookie2","hello cookie2");
response.addCookie(cookie1);
response.addCookie(cookie2);
}
}
获取Cookie
public class getCookie extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response){
try {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Cookie[] cookies=request.getCookies();
for(int i=0;i<cookies.length;i++){
try {
System.out.println("name="+cookies[i].getName()+" value="+ URLDecoder.decode(cookies[i].getValue(),"utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
2、Session 原理
Cookie的问题
- 不安全,可以被冒用,涂改
- 不能存储大量信息,一般不超过4K
- 只能存储文本
- Cookie保存在客户端不安全,不能存储敏感信息,如: 密码·
Session 在Cookie基础之上解决了安全问题。
Session解决问题的方案类似于 银行卡!
- Session将数据存储在 服务器 端
- Session利用Cookie存储了一个 号码,用于识别用户身份,称为SessionID
- 服务端设置了一个集合,这个集合就是Session,并且与SessionID绑定
- 第一次创建Session时候,服务器创建Session集合对象并且分配Session的ID,将Session ID自动下发到 浏览器Cookie,浏览器关闭,Session中存储的数据也还存在,但重启浏览器后,由于之前的Cookie被清除,Session中的数据拿不到。
- 第二次请求时候浏览器通过Cookie传送回 SessionID,服务器会找到对应Session集合。 为用户提供存储服务。
- 当客户端SessionID丢失时或者浏览器离线以后,服务器会定时销毁Session。 默认是30分钟.
Java 提供了Session API
HttpSession session = request.getSession();-为什么是获取session,服务端设置了一个集合,这个集合就是Session,
session.setAttribute("message", "Hello World!");
设置Session
public class SetSession extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response){
try {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
HttpSession session=request.getSession();
session.setAttribute("message","你好Session");
}
}
获取Session
public class getSession extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response){
try {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
HttpSession session=request.getSession();
String st=(String)session.getAttribute("session");
System.out.println(st);
}
}
利用Session与Cookie技术的登录案例:
业务逻辑:只有数据库中的用户才能有增删改查用户的权限,在进入用户管理界面前需要进行登录状态的验证,当处于已登录状态时,可以正常进入用户管理界面;未处于已登录状态时,需要跳转到登录窗口进行登录,在相应位置填好用户信息后点击登录按钮,将用户信息发送到后台,与数据库中用户信息进行比较,若能匹配上,设置Session,跳转回第一个界面,点击用户管理再次进行登录状态的验证;若匹配不上,返回登陆界面,提示用户名或密码错误。
验证登录:
- 请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1><a href="user/ifLogin">用户管理</a></h1>
</body>
</html>
- web.xml
<servlet>
<servlet-name>ifLogin</servlet-name>
<servlet-class>controller.IfLogin</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ifLogin</servlet-name>
<url-pattern>/user/ifLogin</url-pattern>
</servlet-mapping>
- controller层
package controller;
import bean.User;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class IfLogin extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session=request.getSession();
User user= (User) session.getAttribute("user");
String path=request.getContextPath();
if(user==null){
request.getRequestDispatcher("/login.jsp").forward(request,response);
}else {
response.sendRedirect(path+"/user/getUsers");
}
}
}
实现登录
- 登录界面
<%@ page import="java.net.URLDecoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%
Cookie[] cookies=request.getCookies();
for(Cookie cookie:cookies){
if("name".equals(cookie.getName())){
request.setAttribute("name", URLDecoder.decode(cookie.getValue(),"utf-8"));
}
}
%>
</head>
<body>
${message}
<form action="login" method="get">
用户名:<input type="text" name="name" value="${name}"><br>
密 码:<input type="password" name="password"><br>
记住用户名<input type="checkbox" name="memory" value="1"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
- web.xml
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>controller.Login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/user/login</url-pattern>
</servlet-mapping>
- controller层
package controller;
import bean.User;
import service.UserService;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.net.URLEncoder;
public class Login extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name=request.getParameter("name");
String password=request.getParameter("password");
int memory=Integer.parseInt(request.getParameter("memory"));
HttpSession session=request.getSession();
UserService userService=new UserService();
User user=userService.findUserByNameAndPassword(name,password);
if(memory==1){
Cookie cookie=new Cookie("name", URLEncoder.encode(name,"utf-8"));
cookie.setMaxAge(60*60);
response.addCookie(cookie);//很容易忘!!!
}
if(user==null){
request.setAttribute("message","用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}else {
System.out.println(user.toString());
String path=request.getContextPath();
session.setAttribute("user",user);
response.sendRedirect(path+"/index.jsp");
}
}
}
- service层
public User findUserByNameAndPassword(String name, String password) {
return userDao.findUserByNameAndPassword(name,password);
}
- dao层
public User findUserByNameAndPassword(String name, String password) {
User user=new User();
Connection connection=DBUtil.getConnection();
String sql="select * from user where name=? and password=?";
PreparedStatement ps=null;
try {
ps=connection.prepareStatement(sql);
ps.setString(1,name);
ps.setString(2,password);
ResultSet rs=ps.executeQuery();
while(rs.next()){
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
return user;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection);
}
return null;
}