目录
- 表白墙
- Cookie和Session
- 实现用户登录
- 上传文件
表白墙
【服务器版的表白墙】
在之前通过前端代码实现的表白墙有一个问题,当我们关闭页面后,表白的数据也就丢失了,下面我们要做的是做一个服务器版的表白墙,这样即使关闭页面,表白墙的内容也不会丢失
(1)创建Maven项目他,添加目录,引入依赖,将前端页面拷贝到项目中
此时启动Tomcat,就可以通过网络的方式访问表白强页面
(2)约定前后端接口
-
页面加载的时候,从后端读取数据
请求:GET /message
响应:返回json格式的数据 -
用户点击提交的时候,把数据传给后端进行保存
请求:POST /message
响应:返回ok
(3)实现前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表白墙</title>
<style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
width: 800px;
margin: 10px auto;
}
.container h2{
text-align: center;
margin: 10px;
}
.row {
display: flex;
height: 50px;
justify-content: center;
}
.row span{
height: 50px;
width: 100px;
line-height: 50px;
}
.row input{
height: 50px;
width: 300px;
}
.row button{
width: 400px;
height: 50px;
margin: 20px;
border: none;
border-radius: 10px;
}
.row button:active{
background-color: green;
}
</style>
</head>
<body>
<div class="container">
<h2>表白墙</h2>
<div class="row">
<span>谁</span>
<input type="text" id="from">
</div>
<div class="row">
<span>对谁</span>
<input type="text" id="to">
</div>
<div class="row">
<span>说什么</span>
<input type="text" id="message">
</div>
<div class="row">
<button>提交</button>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
<script>
let container=document.querySelector('.container');
let button=document.querySelector('button');
let fromInput=document.querySelector('#from');
let toInput=document.querySelector('#to');
let messageInput=document.querySelector('#message');
button.onclick=function(){ //点击提交按钮后,触发后续的提交数据的请求
let from=fromInput.value;
let to=toInput.value;
let message=messageInput.value;
if(from==null || to==null ||message==null){
return ;
}
let newDiv=document.createElement('div');
newDiv.className="row";
newDiv.style='margin:20px'
newDiv.innerHTML=from + "对"+to +"说"+message;
container.appendChild(newDiv);
let messageJson={ //构造一个js对象
"from":from,
"to":to,
"message":message
};
//把输入框中的数据构造成post请求提交给服务器端
$.ajax({
type:'post',
url:'message',
contentType:'application/json;charset=utf8',
data:JSON.stringify(messageJson), //将js对象通过函数转成json格式的字符串
success:function(body){
alert("提交成功!!!"); //提交成功后有一个弹窗提示
}
});
}
function load(){
$.ajax({
type:'GET',
url:'message',
success:function(body){
// 此处的body是一个js对象的数组
// ajax自动进行类型转换,将json格式的字符串解析成js对象
let container=document.querySelector(".container");
for(let message of body){ //遍历js对象数组,一次显示到页面上
let newdiv=document.createElement('div');
newdiv.className='row';
newdiv.innerHTML=message.from+" 对 "+message.to+" 说 "+message.message;
container.appendChild(newdiv);
}
}
});
}
load(); //访问页面时,就会调用该函数,该函数内部会给服务器发一个GET请求,获取数据并显示到页面上
</script>
</body>
</html>
(4)实现服务器端代码(数据保存到数据库中)
创建Message类
class Message{
public String from;
public String to;
public String message;
}
与数据库建立和关闭连接的类
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//用来进行数据库连接
public class DBUtil {
private static DataSource dataSource=null;
private static DataSource getDataSource(){
if(dataSource==null){
dataSource=new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/MessageWall?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("wp143212");
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
在数据库中创建message表的sql语句
create database MessageWall;
use MessageWall;
drop table if exists message;
create table message(
`from` varchar(100),
`to` varchar(100),
message varchar(100)
);
MessageServlet
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.JDBC4PreparedStatement;
import com.sun.scenario.effect.impl.sw.java.JSWColorAdjustPeer;
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;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
class Message{
public String from;
public String to;
public String message;
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
private ObjectMapper objectMapper=new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//将服务器中数据返回给客户端
resp.setContentType("application/json;charset=utf8");
List<Message> messageList=load();
String respString=objectMapper.writeValueAsString(messageList);//此处messageList是一个List,
//被转成一个json数组
resp.getWriter().write(respString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//将客户端传来的数据保存到服务器端
Message message= objectMapper.readValue(req.getInputStream(),Message.class);
save(message);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write("{ \"ok\":1 }");
}
//把消息保存到数据库中
private void save( Message message ){
Connection connection=null;
PreparedStatement statement=null;
try {
//1.和数据库建立连接
connection=DBUtil.getConnection();
//2.构造SQL语句
String sql="insert into message values(?,?,?)";
statement=connection.prepareStatement(sql);
statement.setString(1,message.from);
statement.setString(2,message.to);
statement.setString(3,message.message);
//4.执行sql语句
int ret=statement.executeUpdate();
if(ret!=1){
System.out.println("插入失败");
}else {
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//4.关闭连接
DBUtil.close(connection,statement,null);
}
}
//从数据库中查询记录
private List<Message> load(){
List<Message> list=new LinkedList<>();
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
//1.和数据库建立连接
connection=DBUtil.getConnection();
//2.构造SQL语句
String sql="select * from message";
statement=connection.prepareStatement(sql);
//3.执行sql
resultSet=statement.executeQuery();
//4.遍历结果集
while (resultSet.next()){
Message message=new Message();
message.from=resultSet.getString("from");
message.to=resultSet.getString("to");
message.message=resultSet.getString("message");
list.add(message);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//5.关闭连接
DBUtil.close(connection,statement,resultSet);
}
return list;
}
}
页面效果
Cookie和Session
Cookie是浏览器,给HTTP协议提供的一个持久化存储数据的方案,Cookie是存储的键值对,不能存复杂的对象,只能存字符串,Cookie是按照域名进行分类存储的,
Cookie中的数据从哪来:Cookie中的数据来自于服务器,服务器代码想让浏览器存啥,就返回啥(通过在HTTP响应报文的header加入Set-Cookie属性)
Cookie中的数据到哪去:回到服务器去,每次给服务器发送的HTTP请求,就会带上之前的Cookie信息(在请求的header中带有Cookie属性)
【Cookie和Sessoin的典型应用】:保存用户的身份信息
实现方式有下面两种
- 可以让服务器把用户的所有信息通过Set-Cookie返回给浏览器,直接在浏览器保存(不推荐,敏感数据在网络上传输存在风险)
- 在服务器中保存用户的详细信息,使用键值对的方式进行保存,key是服务器自动生成的一串唯一的字符串,值就是用户的详细信息,服务器把key通过Set-Cookie 返回给浏览器,因此浏览器的Cookie中存储的只是一个随机的字符串,大大提升了安全性能
在服务器端用来存储用户身份的键值对就叫做session(会话),key就是sessionId
【黑客如果截取了sessionId,不还是能够登陆吗?】
为了解决这个问题,就需要给sessionId加上"时间约束"和"空间约束"
时间约束:同一个用户,登录之后得到的sessionId只能在一定时间内有效,超出时间就需要重新登录
空间约束:必须是通过哪个设备使用的这个sessionId才有效,换了一个设备,就需要重新登录
【Cookie和Session的区别】
- Cookie和客户端机制,Session是服务器端机制
- Cookie和Session经常配合使用,但不是必须配合
- 完全可以用Cookie来存储一些数据在客户端(例如上次登录时间等),这些数据不一定是用户信息,也不一定是sessionId
- Session中的sessionId也不需要非得通过Cookie/Set-Cookie来传递
【HttpServletRequest 类中的Cookie相关方法】
getSessoin方法:
- 首先尝试取出Cookie中的sessionId
- 拿着sessionId去服务器内部维护的哈希表里面查找对应的结果
如果能查到,就会返回一个HttpSession对象
如果获取不到sessionId或拿着sessionId没有找到value,分以下两种情况
(1)直接返回NULL(参数传false采取的做法)
(2)创建一个HttpSession对象作为value,生成一个新的sessionId作为key,把这个键值对存入到哈希表中 (参数为true采取的做法) - 将sessionId返回给浏览器保存
【HttpServletResponse 类中的相关方法】
【HttpSession类中的相关方法】
【Cookie类中相关方法】
每个Cookie对象就是一个键值对
实现用户登录
在要实现的程序中包含一个主页和登录页,登录页中可以输入用户名和密码,服务器验证正确就跳转到主页,在主页中显示出当前用户的身份信息和访问次数
主页:通过Servlet动态生成,因为每次访问后得到的访问次数都不相同
登录页:静态的页面
(1)约定前后端接口
请求:POST /login
通过form表单的形式提交用户名和密码
(2)前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页</title>
</head>
<body>
<form action="login" method="POST">
<input type="text" name="username">
<input type="text" name="password" >
<input type="submit" value="提交">
</form>
</body>
</html>
(3)后端代码,处理前端发来的POST请求
LoginServlet:处理登录请求,验证用户名和密码是否正确
package login;
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("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf8");
//读取请求中的参数
String username=req.getParameter("username");
String password=req.getParameter("password");
if(username==null || username.equals("") || password==null || password.equals("")){
//前端发来的参数不完整
resp.getWriter().write("用户名或密码不完整");
return;
}
//验证用户名和密码是否正确,正常情况下我们是需要从数据库中取数据然后判定
//此处我们简化了这个过程
if(username.equals("zhangsan") && password.equals("123")){
//登录成功
//创建一个会话,将用户信息填写到Session中
HttpSession session=req.getSession(true);
session.setAttribute("username","zhangsan");
session.setAttribute("visitCount",0);
//跳转到主页
resp.sendRedirect("index");
}else{
resp.getWriter().write("用户名或密码错误");
return;
//登录失败
}
}
}
IndexServlet:返回当前登录用户信息和登录次数
package login;
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("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//把当前的用户信息展示到页面上
HttpSession session= req.getSession(false);
if(session==null){
//说明当前用户未登录,需要跳转到登录页面进行登录
resp.sendRedirect("login.html");
return;
}
String username=(String)session.getAttribute("username");
Integer visitCount=(Integer) session.getAttribute("visitCount");
if(visitCount==null){
session.setAttribute("visitCount",0);
}
visitCount++;
session.setAttribute("visitCount",visitCount);
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前用户为:"+username+" 访问次数: "+visitCount);
}
}
【通过抓包来看多次交互过程】
第一次请求:访问登录页面,是不带有Cookie的
第一次响应:也是不带有Set-Cookie的
第二次请求:输入用户名和密码后进行登录
第二次响应:Set-Cookie字段中带有sessionId,这个sessionId就会保存到浏览器的Cookie中,同时返回了一个重定向地址,
第三次请求:请求中带有Cookie字段
第三次响应:返回用户的信息
上传文件
使用到的HttpServletRequest类方法
Part类方法
(1)创建一个upload.html,并放在webapp目录下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传文件</title>
</head>
<body>
<form action="upload" method="POST" enctype="multipart/form-data">
<input type="file" name="MyFile">
<input type="submit" value="上传">
</form>
</body>
</html>
- 在 form 中要加上 multipart/form-data 字段,表示当前正在上传文件
- input 标签中name属性的值就是getPart(String var1) 要传的参数
(2)实现后端代码,创建UploadServlet类,将客户端上传的文件保存到服务器端的磁盘中
package upload;
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.Part;
import java.io.IOException;
@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Part part=req.getPart("MyFile"); //获取文件
System.out.println(part.getContentType()); //获取文件类型
System.out.println(part.getSize()); //获取文件大小
System.out.println(part.getSubmittedFileName()); //获取文件名
//把文件写入到服务器这边的磁盘中
part.write("d:/result.jpg");
resp.getWriter().write("upload ok!!!!!");
}
}
- 需要给 UploadServlet 加上 @MultipartConfig 注解. 否则服务器代码无法使用 getPart 方法
- getPart 的 参数 需要和 form 中 input 标签的 name 属性对应.
- 客户端一次可以提交多个文件. (使用多个 input 标签). 此时服务器可以通过 getParts 获取有的Part 对象.
(3)测试程序
- 访问upload.html页面,并提交文件
- 上传文件"计算机视觉.pdf",点击上传后,服务器返回 upload ok
- 在 d 盘中发现 result.jpg文件
- 抓包结果