请预习 Cookie、Session、Cache-Control 等 HTTP 知识
- https://zh.wikipedia.org/wiki/Cookie
- https://zhuanlan.zhihu.com/p/22396872?refer=study-fe
- Session 维基百科:
https://zh.wikipedia.org/wiki/%E4%BC%9A%E8%AF%9D_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)
- Cache-Control
课上的代码:https://github.com/FrankFang/sign-in-demo
要点:
Cookie 的特点
- 服务器通过 Set-Cookie 响应头设置 Cookie
- 浏览器得到 Cookie 之后,每次请求都要带上 Cookie
- 服务器读取 Cookie 就知道登录用户的信息(email)
问题
- 我在 Chrome 登录了得到 Cookie,用 Safari 访问,Safari 会带上 Cookie 吗
no - Cookie 存在哪
Windows 存在 C 盘的一个文件里 - Cookie会被用户篡改吗?
可以,下节课会讲 Session 来解决这个问题,防止用户篡改 - Cookie 有效期吗?
默认有效期20分钟左右,不同浏览器策略不同
后端可以强制设置有效期,具体语法看 MDN - Cookie 遵守同源策略吗?
也有,不过跟 AJAX 的同源策略稍微有些不同。
当请求 qq.com 下的资源时,浏览器会默认带上 qq.com 对应的 Cookie,不会带上 baidu.com 对应的 Cookie
当请求 v.qq.com 下的资源时,浏览器不仅会带上 v.qq.com 的Cookie,还会带上 qq.com 的 Cookie
另外 Cookie 还可以根据路径做限制,请自行了解,这个功能用得比较少。
1. 复习HTTP请求响应相关初级知识
这里的cookie、cache-control指的是响应头中的
- js发请求
//js发请求(使用XMLHttpRequest)
var request = new XMLHttpRequest()
request.open('get','/xxx') //配置request(get请求)
request.send()
//request.open('post','/xxx') //配置request(post请求)
//request.send('a=1&b=2')
request.onreadystatechange=function(){
if(request.readyState===4){
console.log('请求响应都完成了')
if(request.status >= 200 && request.status<300 ){
console.log('请求成功')
}
}
}
-
Node.js接收请求并响应
// 后端代码
if(path==='/xxx'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:8001')
response.write(`
{
"note":{
"to": "小谷",
"from": "方方",
"heading": "打招呼",
"content": "hi"
}
}
`)
response.end()
}
2. 实现注册登录的功能
promise????
此时发送post请求,服务端会返回什么结果?
——选B 一串html
服务端代码更改如上图,此时发送post请求,服务端会返回什么结果?
——选A 404
那么Node.js怎么读取post请求的数据?然后做相应的处理呢?
获取用户请求的第四部分:
有点复杂,因为请求体是一段一段上传的,不是一下子上传的,所以要监听data事件,当监听到end的时候,将之前的数据接收到的数据都连接起来,获取完整的请求数据。
打印的请求体打印在服务端的控制台
使用promise封装一个读取请求body的方法readBody
- 服务端解析请求体
//服务端解析请求体
把字符串格式的:email=1&password=2&password_confirmation=3
解析成hash格式的:{email:'1',password:'2',password_confirmation:'3'}
//ES5的写法
let email=hash[email]
let password=hash[password]
let password_confirmation=hash[password_confirmation]
//ES6的写法
let {email,password,password_confirmation}=hash //ES6的写法
再加上客户端和服务端的数据验证、提示等
详见 课上的代码:https://github.com/FrankFang/sign-in-demo
//服务端代码
}else if(path === '/sign_up' && method === 'POST'){
readBody(request).then((body)=>{
let strings = body.split('&') // ['email=1', 'password=2', 'password_confirmation=3']
let hash = {}
strings.forEach((string)=>{
// string == 'email=1'
let parts = string.split('=') // ['email', '1']
let key = parts[0]
let value = parts[1]
hash[key] =value // hash['email'] = '1'
})
let {email, password, password_confirmation} = hash
if(email.indexOf('@') === -1){//校验邮箱格式
response.statusCode = 400
response.setHeader('Content-Type', 'application/json;charset=utf-8')
response.write(`{
"errors": {
"email": "invalid"
}
}`)
}else if(password !== password_confirmation){//校验密码和确认密码是否一致
response.statusCode = 400
response.write('password not match')
}else{//通过校验,返回200,注册成功
response.statusCode = 200
response.write('success sign up')
}
response.end()
})
}
//客户端代码
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('')
})
//校验各个字段非空,为空的话显示对应的提示信息并return
if(hash['email'] === ''){
$form.find('[name="email"]').siblings('.error')
.text('填邮箱呀同学')
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('密码不匹配')
return
}
$.post('/sign_up', hash)
.then((response)=>{
console.log(response)
}, (request)=>{
let {errors} = request.responseJSON
if(errors.email && errors.email === 'invalid'){
$form.find('[name="email"]').siblings('.error')
.text('邮箱格式错误')
}
})
})
这些校验前后端都要做!!!不能只是前端做,后端也要做的
因为可以通过curl不通过前端的js直接就请求到后端,所以后端一定要做相应的校验!!!
3. Cookie
这里有个问题,就是邮箱里面的@符号,在http请求中会变成%40
你以为有@,其实实际上没有@
不做处理的话,会一直报邮箱格式错误。怎么处理呢?
—— decodeURIComponent : 将已编码 URI 中所有能识别的转义序列转换成原字符。
当服务器发现邮箱、密码之类的都格式没问题了之后,需要做什么?
- 创建文件db/users 用来存用户数据,当做数据库
- 注册的时候验证没问题就把邮箱和密码存到数据库中
- 新增users数据的时候需要考虑这个用户是否已经存在
- 数据库中不应该存用户的明文密码,这样不安全,应该存加密后的密码,下次登录的时候将用户传过来的明文密码加密后和数据库中的密文密码比对,就可以判断密码是否正确了
- 加一个登录页面
//注册接口服务端
//邮箱密码什么的都验证没问题后
}else{
var users = fs.readFileSync('./db/users', 'utf8')
try{
users = JSON.parse(users) // []
}catch(exception){
users = []
}
let inUse = false
for(let i=0; i
添加登录页面
- 服务端代码处理登录
//server.js
//sign_in的get请求,响应登录页面(html)
}else if(path==='/sign_in' && method === 'GET'){
let 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('&') // ['email=1', 'password=2', 'password_confirmation=3']
let hash = {}
strings.forEach((string)=>{
// string == 'email=1'
let parts = string.split('=') // ['email', '1']
let key = parts[0]
let value = parts[1]
hash[key] = decodeURIComponent(value) // hash['email'] = '1'
})
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
- 客户端代码-发送登录post请求
//sign_in.html
//发送sign_in的post请求,提交登录数据
$.post('/sign_in', hash)
.then((response)=>{
//登录成功,进入首页
window.location.href = '/'
}, (request)=>{
alert('邮箱与密码不匹配')
})
现在有一个问题就是,如果不通过登录页面,直接输入首页的url也可以进入首页。这样不是我们想要的效果,我们希望登录用户才可以进入首页,同时首页显示登录用户信息,这就要引入cookie
if(found){
response.setHeader('Set-Cookie', `sign_in_email=${email}`) //!!!!加这一句
response.statusCode = 200
}
-
响应头多返回了个Set-Cookie↓
-
后面同源的请求头也就默认多了个cookie
Cookie的特点
- 服务器通过Set-Cookie响应头设置Cookie
- 浏览器得到Cookie之后,每次请求都要带上Cookie(同源下?)
- 服务器读取请求头的Cookie就知道登录用户的信息(email)
问题
- 我在 Chrome 登录了得到 Cookie,用 Safari 访问,Safari 会带上 Cookie 吗
no - Cookie 存在哪
Windows 存在 C 盘的一个文件里 - Cookie会被用户篡改吗?
可以,下节课会讲 Session 来解决这个问题,防止用户篡改 -
Cookie 有有效期吗?
默认有效期20分钟左右,不同浏览器策略不同
后端可以强制设置有效期,具体语法看 MDN
- Cookie 遵守同源策略吗?
也有,不过跟 AJAX 的同源策略稍微有些不同。
当请求 qq.com 下的资源时,浏览器会默认带上 qq.com 对应的 Cookie,不会带上 baidu.com 对应的 Cookie
当请求 v.qq.com 下的资源时,浏览器不仅会带上 v.qq.com 的Cookie,还会带上 qq.com 的 Cookie
另外 Cookie 还可以根据路径做限制,请自行了解,这个功能用得比较少。
如何在首页显示登录用户信息?
-
nodejs read cookie
console.log(request.headers.cookie)
//server.js
//服务端读取cookie
//在首页显示登录用户的信息(密码)
if(path === '/'){
let string = fs.readFileSync('./index.html', 'utf8')
let cookies = request.headers.cookie.split('; ') // ['[email protected]', 'a=1', 'b=2']
let hash = {}
for(let i =0;i
代码细节不用那么清楚,主要要搞清楚流程