注册登录与cookie

准备工作

首先建立一个 server.js 和 一个 index.html 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]

if(!port){
console.log('请指定端口号好不啦?\nnode server.js 8888 这样不会吗?')
process.exit(1)
}

var server = http.createServer(function(request, response){
var parsedUrl = url.parse(request.url, true)
var pathWithQuery = request.url
var queryString = ''
if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
var path = parsedUrl.pathname
var query = parsedUrl.query
var method = request.method

/******** 从这里开始看,上面不要看 ************/

console.log('得到 HTTP 路径\n' + path)

if(path === '/'){
var string = fs.readFileSync('./index.html','utf8')
response.statusCode = 200
response.setHeader('Content-Type','text/html; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js','utf8')
response.statusCode = 200
response.setHeader('Content-Type','text/javascript; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/jQuery-ajax.js'){
var string = fs.readFileSync('./jQuery-ajax.js','utf8')
response.statusCode = 200
response.setHeader('Content-Type','text/javascript; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/promise.js'){
var string = fs.readFileSync('./promise.js','utf8')
response.statusCode = 200
response.setHeader('Content-Type','text/javascript; charset=utf-8')
response.write(string)
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(`
{
"error": "not found"
}
`)
response.end()
}

/******** 代码结束,下面不要看 ************/
})

server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)

我们要加入一个 注册 页面的路由,创建 sign_up.html 文件

sign_up.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
border: 1px solid red;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.form-wrapper{
min-width: 350px;
padding: 20px;
border: 1px solid #ddd;
}
.form-wrapper .row{
margin: 10px 0;
}
.form-wrapper .row>label{
display: inline-block;
min-width: 4em;

}
</style>
</head>
<body>
<div class="form-wrapper">
<h1>注册</h1>
<form id="signUpForm">
<div class="row">
<label>邮箱</label>
<input type="text" name="email">
<span class="error"></span>
</div>
<div class="row">
<label>密码</label>
<input type="password" name="password">
<span class="error"></span>
</div>
<div class="row">
<label>确认密码</label>
<input type="password" name="password_confirmation">
<span class="error"></span>
</div>
<div class="row">
<input type="submit" value="注册">
</div>
</form>
</div>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
let $form = $('#signUpForm')
$form.on('submit', (e) => {
e.preventDefault() //阻止自己提交
let hash = {}
let need = ['email','password','password_confirmation']
need.forEach((name) => {
let value = $form.find(`[name=${name}]`).val() // 分别获得,我们在邮箱,密码,确认密码中输入的值
hash[name] = value
})
$form.find('.error').each((index, span) => {
$(span).text('')
})
if(hash['email'] === ''){
$form.find('[name="email"]').siblings('.error').text('请输入邮箱').css({"color":"red"})
return
}
if(hash['password'] === ''){
$form.find('[name="password"]').siblings('.error').text('请输入密码')
return
}
if(hash['password_confirmation'] === ''){
$form.find('[name="password_confirmation"]').siblings('.error').text('请确认密码')
return
}
if(hash['password'] !== hash['password_confirmation']){
$form.find('[name="password_confirmation"]').siblings('.error').text('密码不一致').css({"color":"red"})
return
}

$.post('/sign_up', hash) //发送注册请求之后
.then((response) => {
console.log(response)
}, (request) => {
// console.log(request.statusText, request.status)
let {errors} = request.responseJSON //{errors} 邮箱有误的对象的信息
if(errors.email && errors.email === 'invalid'){
$form.find('[name=email]').siblings('.error').text('邮箱格式有误').css({"color":"red"})
}
})
})
</script>
</body>
</html>

将以下代码添加进 server.js 文件中

1
2
3
4
5
6
7
else if(path === '/sign_up' && method === 'GET'){ //当打开 sign_up,并且 method === 'GET' 时
var string = fs.readFileSync('sign_up.html','utf8')
response.statusCode = 200
response.setHeader('Content-Type','text/html; charset=utf-8')
response.write(string)
response.end()
}

现在我们可以通过 node server 8080 启动

node server 8080

然后在浏览器地址栏 打开 http://localhost:8080/sign_up

http://localhost:8080/sign_up

将以下代码添加进 server.js 文件中(封装一个函数,用来读取第四部分 Form Data 里面的内容)

1
2
3
4
5
6
7
8
9
10
11
12
//node http get post data
function readBody(request){
let body = [] //
return new Promise((resolve, reject) => {
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
resolve(body)
})
})
}

将以下代码添加进 server.js 文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
else if(path === '/sign_up' && method === 'POST'){//当打开 sign_up,并且 method === '' 时
readBody(request).then((body) => { //读取输入的数据(email=1%40&password=1&password_confirmation=1)
let strings = body.split('&') //用 split 分离(分别输入1@,1,1 得到[ 'email=1%40', 'password=1', 'password_confirmation=1' ])
let hash = {}
strings.forEach((string) => {
let parts = string.split('=') //[ 'email', '1%40' ][ 'password', '1' ][ 'password_confirmation', '1' ]
let key = parts[0] //email password password_confirmation
let value = parts[1] //1%40 1 1
hash[key] = decodeURIComponent(value) //decodeURIComponent 用于将@ 转换(hash['email']='1@')
})

let {email, password, password_confirmation} = hash
if(email.indexOf('@') === -1){ //如果 email 里面没有@
response.statusCode = 400 //那么就 400
response.setHeader('Content-Type','application/json; charset=utf-8') //此处标明为 JSON语法
// 以下代码是后端的
response.write(`{
"errors":{
"email": "invalid"
}
}`)
}else if(password !== password_confirmation){ //如果 密码 与 确认密码不一样
response.statusCode = 400
response.write('password is not same')
}else{
var users = fs.readFileSync('./db/users', 'utf8') //users
// 如果 有问题 就 清空
try{
users = JSON.parse(users) //[]
}catch(exception){
users = []
}

// 用户是否已存在
let inUse = false //先默认没有注册
for(let i=0;i<users.length;i++){
let user = users[i]
if(user.email === email){
inUse = true //如果被占用
break; //阻止注册
}
}
if(inUse){ //如果用户被占用
response.statusCode = 400 //提示错误400
response.write('email in use') //并提示 'email in use'
}else{ //如果没有被占用
users.push({email: email, password: password})
var usersString = JSON.stringify(users)
fs.writeFileSync('./db/users', usersString)
response.statusCode = 200
}
}
response.end()
})
}

注:前后端的代码都应该验证,后端一定要验,前端不一定。黑客可以跨过前端JS通过 curl 发请求,所以一定要确保后端代码。
curl
粘贴

再新建一个 db 文件夹,加入 users 文件,将 users 作为我们的数据库

users

等以上设置好了,注册用户,回到 user 文件,则可以看到 注册的用户信息。

现在我们要做一个 登录 页面,创建 sign_in.html 文件

sign_in.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
border: 1px solid red;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.form-wrapper{
min-width: 350px;
padding: 20px;
border: 1px solid #ddd;
}
.form-wrapper .row{
margin: 10px 0;
}
.form-wrapper .row>label{
display: inline-block;
min-width: 4em;

}
</style>
</head>
<body>
<div class="form-wrapper">
<h1>登录</h1>
<form id="signInForm">
<div class="row">
<label>邮箱</label>
<input type="text" name="email">
<span class="error"></span>
</div>
<div class="row">
<label>密码</label>
<input type="password" name="password">
<span class="error"></span>
</div>
<div class="row">
<input type="submit" value="登录">
</div>
</form>
</div>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
let $form = $('#signInForm')
$form.on('submit', (e) => {
e.preventDefault()
let hash = {}
let need = ['email','password','password_confirmation']
need.forEach((name) => {
let value = $form.find(`[name=${name}]`).val()
hash[name] = value
})
$form.find('.error').each((index, span) => {
$(span).text('')
})
if(hash['email'] === ''){
$form.find('[name="email"]').siblings('.error').text('请输入邮箱').css({"color":"red"})
return
}
if(hash['password'] === ''){
$form.find('[name="password"]').siblings('.error').text('请输入密码')
return
}
$.post('/sign_in', hash)
.then((response) => {
window.location.href = '/'
}, (request) => {
alert('失败')
})
})
</script>
</body>
</html>

将以下代码添加进 server.js 文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
else if(path === '/sign_in' && method === 'GET'){
var string = fs.readFileSync('sign_in.html','utf8')
response.statusCode = 200
response.setHeader('Content-Type','text/html; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/sign_in' && method === 'POST'){
readBody(request).then((body) => {
let strings = body.split('&')
let hash = {}
strings.forEach((string) => {
let parts = string.split('=')
let key = parts[0]
let value = parts[1]
hash[key] = decodeURIComponent(value) //decodeURIComponent 用于将@ 转换
})
let {email, password} = hash
var users = fs.readFileSync('./db/users', 'utf8')
try{
users = JSON.parse(users) //[]
}catch(exception){
users = []
}
let found
for(let i=0;i<users.length;i++){
if(users[i].email === email && users[i].password === password){
found = true
break;
}
}
if(found){
response.setHeader('Set-Cookie', `sign_in_email=${email}; HttpOnly`) // HttpOnly,禁止用户擅自修改cookie
response.statusCode = 200
}else{
response.statusCode = 401
}
response.end()
})
}

Set-Cookie 的用法
Set-Cookie

现在登录一个已经注册过的用户,在点击 登录 之前,钩上 Preserve log
Preserve log
可以看到sign_in, Response Headers 里面的 Set-Cookielocalhost, Request Headers 里面的 Cookie 内容一致。

sign_in
localhost

  1. 服务器通过 Set-Cookie 响应头设置Cookie
  2. 浏览器得到 Cookie 之后,每次请求都要带上 Cookie
  3. 服务器读取 Cookie 就知道登录用户的信息(email)