动态网络服务器(dynamic web server)
由一个静态的网络服务器加上额外的软件组成,最普遍的是一个应用服务器和一个数据库
我们称它为 “动态” 是因为这个应用服务器会在通过 HTTP 服务器把托管文件传送到你的浏览器
之前会对这些托管文件进行更新。
构建一个模拟的数据库,结构是一个数组 /db/user.json
[
{"id":"1", "name":"frank", "password":"***", "age":"18"},
{"id":"2", "name":"jack", "password":"***", "age":"20"},
]
获取数据库的内容
const fs = require("fs");
读数据库
const usersString = fs.readFileSync("./db/users.json").toString();
const usersArray = JSON.parse(usersString);
写数据库
const users = { id: 3, name: 'tom', password: 'yyy' }
usersArray.push(users)
const string = JSON.stringify(usersArray)
fs.writeFileSync('./db/users.json', string)
实现用户注册功能
用户提交用户名和密码,users.json里新增一行数据
思路
前端写一个form,让用户填写name和password
前端监听submit事件
前端发送post请求,数据位于请求体
后端接收post数据
后端获取请求体中的name和password
后端储存数据
代码
<body>
<form id="registerForm">
<div>
<label>用户名<input type="text" name="name"></label>
</div>
<div>
<label>密码<input type="password" name="password"></label>
</div>
<div>
<button type="submit">注册</button>
</div>
</form>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
const $form = $('#registerForm')
$form.on('submit', (e) => {
e.preventDefault()
const name = $form.find('input[name=name]').val()
const password = $form.find('input[name=password]').val()
$.ajax({
method: 'POST',
url: '/register',
contentType: 'text/json; charset=UTF-8',
data: JSON.stringify({ name, password })
}).then(() => {
alert('注册成功')
location.href = '/sign_in.html'
}, () => { }
)
})
</script>
</body>
实现用户登录功能
首页home.html,已登录用户可以看到自己的用户名
登录页sign_in.html,供提交用户名和密码
输入的用户名和密码如果是匹配的,就自动跳转到登录页
sign_in.html思路
前端写一个form,让用户填写name和password
前端监听submit事件
前端发送post数据,数据位于请求体
后端接收post请求
后端获取请求体中的name和password
后端读取数据,看是否有匹配的name和password
如果匹配,后端标记用户已登录(如何标记)
代码
<body>
<form id="signInForm">
<div>
<label>用户名<input type="text" name="name"></label>
</div>
<div>
<label>密码<input type="password" name="password"></label>
</div>
<div>
<button type="submit">登陆</button>
</div>
</form>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
const $form = $('#signInForm')
$form.on('submit', (e) => {
e.preventDefault()
const name = $form.find('input[name=name]').val()
const password = $form.find('input[name=password]').val()
$.ajax({
method: 'POST',
url: '/sign_in',
contentType: 'text/json; charset=UTF-8',
data: JSON.stringify({ name, password })
}).then(() => {
alert('登陆成功')
location.href = '/home.html'
}, () => { }
)
})
</script>
</body>
标记用户已登录
cookie定义
cookie是服务器下发给浏览器的一段字符串
浏览器必须保存这个cookie(除非用户删除)
之后发起相同二级域名请求(任何请求)时,浏览器必须附上cookie
Set-Cookie 具体语法
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie
显示用户名
home.html渲染前获得user信息
如果有user,则将{{user.name}} 替换成 user.name
如果无user,则显示登陆按钮
防止篡改user_id
把信息隐藏在服务器
把用户信息放在服务器x里,再给信息一个随机id
把随机id发给服务器
后端下次读到id时,通过x[id]获取用户信息
因为id很长,完全随机,所以无法篡改id
x是文件,不能使用内存,因为断电内存就会清空
这个x又被叫做session(会话)
cookie session总结
服务器可以给浏览器下发cookie
通过Response Header
浏览器上的cookie可以被篡改
用开发者工具就能改
后端下发的cookie用js也能改
服务器下发不可修改的cookie
cookie可包含加密后的信息(需要解密,麻烦,容易被盗)
cookie也可只包含一个id(随机数)
用session[id]可以在后端拿到对应的信息
这个id不会被篡改,但是会被复制
服务器代码
const session = JSON.parse(fs.readFileSync('./session.json').toString())
console.log('有个帅哥发请求过来啦!路径(带查询参数)为:' + pathWithQuery)
if (path === "/sign_in" && method === "POST") {
response.setHeader('Content-Type', 'text/html; charset=utf-8');
const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
const array = [];
request.on("data", chunk => {
array.push(chunk);
});
request.on("end", () => {
const string = Buffer.concat(array).toString();
const obj = JSON.parse(string);
const user = userArray.find((user) => user.name === obj.name &&
user.password === obj.password)
if (user === undefined) {
response.statusCode = 400
response.setHeader('Content-Type', 'text/json; charset=utf-8');
response.end(`{"errorCode:4001"}`)
} else {
response.statusCode = 200
const random = Math.random()
session[random] = { user_id: user.id }
fs.writeFileSync('session.json', JSON.stringify(session))
response.setHeader('Set-Cookie', `session_id=${random}; 'HttpOnly'`)
}
response.end()
});
} else if (path === "/home.html") {
const cookie = request.headers["cookie"];
let sessionId
try {
sessionId = cookie.split(';').filter(s =>
s.indexOf('session_id=') >= 0)[0].split('=')[1]
} catch (error) { }
if (sessionId && session[sessionId]) {
const userId = session[sessionId].user_id
const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
const user = userArray.find(user => user.id === userId);
const homeHtml = fs.readFileSync("./public/home.html").toString();
let string = ''
if (user) {
string = homeHtml.replace("{{loginStatus}}", '已登录')
.replace('{{user.name}}', user.name)
}
response.write(string);
} else {
const homeHtml = fs.readFileSync("./public/home.html").toString();
const string = homeHtml.replace('{{loginStatus}}', '未登录')
.replace('{{user.name}}', '')
response.write(string);
}
response.end()
} else if (path === "/register" && method === "POST") {
response.setHeader('Content-Type', 'text/html; charset=utf-8');
const userArray = JSON.parse(fs.readFileSync("./db/users.json"));
const array = [];
request.on("data", chunk => {
array.push(chunk);
});
request.on("end", () => {
const string = Buffer.concat(array).toString();
const obj = JSON.parse(string);
const lastUser = userArray[userArray.length - 1];
const newUser = {
id: lastUser ? lastUser.id + 1 : 1,
name: obj.name,
password: obj.password
};
userArray.push(newUser);
fs.writeFileSync("./db/users.json", JSON.stringify(userArray));
response.end()
});
} else {
response.statusCode = 200
const filePath = path === '/' ? '/index.html' : path
const index = filePath.lastIndexOf('.')
const suffix = filePath.substring(index)
const fileTypes = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
'.png': 'image/png',
'.img': 'image/jpeg'
}
response.setHeader('Content-type', `${fileTypes[suffix] ||
'text/html'};charset=utf-8`)
let content
try {
content = fs.readFileSync(`./public${filePath}`)
} catch (error) {
content = '文件不存在'
response.statusCode = 404
}
response.write(content)
response.end()
}
})