【前端升全栈】五分钟了解项目开发的登录部分

目录

1.登录开始

 2.cookie简介

3.cookie用于登录验证

4.cookie做限制

5.session介绍 

6.session演示 

7.从session到Redis

8.Redis

9.nodejs链接Redis的demo

10.node.js链接Redis封装工具函数 

11.session存入Redis

12.完成server端登录代码

13. 联调-html界面

14.Nginx配置 


1.登录开始

注册的话其实就像是新增博客,没啥难度,更何况现在都短信注册或者微信扫码等等,没有太大意义。
登录:
  1. 核心:登录校验&登录信息存储
  2. 为何只讲登录,不讲注册?

 Mysql 是硬盘数据库,redis 是内存数据库。

目录:

  1. cookie和session
  2. session写入 redis
  3. 开发登录功能,和前端联调(用到 nainy后向代THP

 2.cookie简介

主要了解什么?

  1. 什么是cookie
  2. javascript操作cookie ,浏览器中查看cookie
  3. server端操作cookie ,实现登录验证

 跨域不共享就是说淘宝和百度的 cookie 是不一样的,各自有各自的 cookie

什么是cookie?

  1. 存储在浏览器的一段字符串(最大5kb )
  2. 跨域不共享
  3. 格式如k1=v1; k2=v2; k3=v3;因此可以存储结构化数据

请求域的 cookie 就是说请求什么如请求百度的 xx 文件就是请求百度的 cookie(不管是在什么页面请求,主要看的是请求的是谁而不是看在什么页面)。

cookie:

  1. 每次发送http请求,会将请求域的cookie一起发送给server
  2. server可以修改cookie并返回给浏览器
  3. 浏览器中也可以通过javascript修改cookie(有限制)
所谓有限制就是服务端返回后将其锁死不愿意让客户端去修改(为了安全)。

 客户端JavaScript操作cookie:

  • 客户端查看cookie ,三种方式
  • javascript查看、修改cookie(有限制)
document.cookie 查看 cookie ,添加的话其实是采用累加的方式 document.cookie = ‘要添加的键值对’(就直接加在 cookie 的后面)。查看 application 下面有 cookie 的详细信息,其中的 path 如果是 / 代表的是根路径,就是说不管我们 url 路径多复杂,都可以使用该 cookie

【前端升全栈】五分钟了解项目开发的登录部分_第1张图片


3.cookie用于登录验证

server端Nodejs操作cookie:

  1. 查看cookie
  2. 修改cookie
  3. 实现登录验证

 回到 app.js,我们来解析 cookie,查看请求的 headers 就可以拿到 cookie,没有就返回空字符串,需要注意的是由于返回的是字符串,使用不是很方便,所以这里还需要解析成对象。 通过split先拆分;拿到每一组,再通过=拆分拿到keyvalue。我们尝试一下,要是想删除 cookie 的话可以前往 application删。

    回到 user.js 我们来规定一下如果 cookie username 就是登 录成功,否则就失败,定义一个测试的路径,记得我们自己所有路由都改成了需要返回 promise ,如果没有封装 promise 可以利用 Promise.resolve 封装一些。那么后端如何修改 cookie ,通过 res setHeader 方法设置 Set-Cookie 和 path=/(如此则所有的网站网页的路由都会生效)。说一下,首先是在 app.js 解析 cookie 方便各地使用。
/解析cookie
req.cookie=//定文一个存放cookie的对象
const cookiestr = req.headers.cookie |l"//拿到cookie字符串或者空字符串cookiestr.split('; ').forEach(item => {//通过;分割出一组cookie
if (litem){
return
const arr = item.split('=')1/通过-分割每一组cookie的key和vaLueconst key = arr[o]
const value = arr[1]req.cookie[key] = value))
console.log(req.cookie)//到此各地皆可以使用req.cookie来获取已经解析完成的cookie了
接着是在 router 里面对是否登录进行验证(主要是查看是否 有 username 存在,注意有可能会因为 username 被当成字符串导致判断失败),这一步可以手动加 cookie ,对测试进行验证.
if (method === 'GET' &8 req.path === '/api/user/login-test') {
  if (req.cookie.username){//通过查看cookie里面有没有username这个key判断是否登录成功(注意有个 
      return Promise.resolve(new successMode1('登录成功'))
return Promise.resolve(new ErrorModel(尚未登录'))
}

 在上面的验证可以生效后我们就可以使用了,在用户登录的 时候同时设置好 cookie,且设置为根路径方便 3000 端口下的都可以访问到,这一步的话由于改成 GET,需要在路径加上密码用户名,访问后就设置好了 cookie,再回 test 测试是否可以成功验证.

【前端升全栈】五分钟了解项目开发的登录部分_第2张图片


4.cookie做限制

然而目前还是有一个问题,就是 js 可以很轻易的设置document.cookie 来覆盖之前的 cookie 伪造身份。 那么怎么去限制呢?很简单,后端设置 cookie 的时候加多一个 httpOnly 即只允许通过服务端修改不允许通过客户端修改。这个时候 HTTP 就会多一个√,且不仅仅是看不见也不 能修改(指的是客户端)。
    按理说不应该被修改,我们最后发现其实是被改的那个前面被加了个空格,这是因为新增cookie 前面都会有这个空格(如果前面有值)很简单,只需要 trim ()去掉几空格就行。 现在在客户端修改的话当然改什么就会在 cookie 出现什么,只不过被锁死的还是锁死,无法被覆盖。 另外,也不应该一直存着 cookie ,应该有个过期时间比较合适,我们定义一个 getCookieExpires 函数,获取当前时间, 并且再加上 24 个小时,返回的话改成 toGMTString 时间格式。 然后就可以回去设置 cookie expire 即过期时间直接等于这个函数就行。
【前端升全栈】五分钟了解项目开发的登录部分_第3张图片

5.session介绍 

将一下 cookie 的问题:危险的个人信息,极小的可存空间。session 是储存登录 / 会话信息的统称,通过 cookie 里面的 id去服务端对应一个具体的信息。
session:
  1. 上—节的问题:会暴露username ,很危险
  2. 如何解决:cookie中存储userid , server 端对应username
  3. 解决方案:session ,即server端存储用户信息

【前端升全栈】五分钟了解项目开发的登录部分_第4张图片 session

 如上图,浏览器中 cookie 提供了 id,传到服务端,服务端可 以有 xxx 对应什么内容,yyy 对应什么内容的约定等等。 回到 app.js 定义一个 SESSION_DATA 的常量,在解析 cookie 后就可以来解析这个 session,判断 cookie 是否有 userId,有 则判断常量里面是否存在 userId,如果不存在则将常量的userId 属性设置为空对象,再赋值给我们的 req.session。如果 cookie 没有 userId 我们就随便定义一个时间戳再去赋值给常量里面的 userId 最后再赋值给我们的 req.session

    这个还是加多一个判断,定义一个 needSetCookie 状态,看看是否需要设置 cookie(cookie 不存在 userId 的时候就要), 实现在 blogs users 的路由,把 userId 设置到 cookie 上。 那么怎么使用呢?这个就需要到登录那里原本设置cookie的可以去掉了,改为设置 session,同时验证登录的时候也改成判断 session

【前端升全栈】五分钟了解项目开发的登录部分_第5张图片


6.session演示 

    上面有报错啊,有两个问题,第一个就是获取 cookie 时间的函数没有拷贝过来,第二个就是状态和 userId 后面都会改不能定义成常量,改成 let ! 注意 userId 的话是不管访问哪个路由都会有(因为路由们都会经过那个 serverHandle 共同的函数)。当然并不是每次刷新都会设置 cookie ,第一次就会,之后判断存在 userId 就不走设置 cookie 的分支了。所以我来捋一下,首先我们访问的是 login-test ,先走的是解析 session ,判断不存在 userId 且定义了 userId (第一次才会,之直接走给 req 赋值 session ),搞定去 login-test 路由,判断当前不存在 username 所以是未登录,回到 users 总路由设置 cookie 。接下来走 login 路由,先判断 session 存在了,赋值给 req ,走 login 路由,填充 session ,完成回到 user 总路由发送内容。再走一次 login-test ,判断 session 中有这个username 所以判断登录成功,自始至终只有 userId 暴露在外,具体拿到这个 userId 怎么处理是只有服务端才能看到。
总计:
  • 知道session解决什么问题;
  • 如何实现session

 所以说 cookie userId 只是为了给我这个专属的 userId 开辟 一个独特的存放 session 的空间,而 session 则是保存了实际的信息。


7.从session到Redis

当前写的 session 还是有问题的。目前本地的 node.js 当然是只有一个进程,但是上线多进程(甚至是多机房什么的)可能导致我们呃,第一次登录到其中一个进程,第二次登录却是另一个进程而失败。
session的问题:
  1. 目前session直接是js 变量,放在nodejs进程内存中
  2. 第一,进程内存有限,访问量过大,内存暴增怎么办?
  3. 第二,正式线上运行是多进程,进程之间内存无法共享

 如图就是进程的内存模型,其中 0x1000 是起始地址,0x8000 是结束地址,stack 储存的是程序运行的基础变量(js 的基础 变量),heap 存的是程序运行的引用变量(js 的数组对象什么的,session 也放在这里),存的越多,占的就越多(越高) 如果上下接起来那就崩了。

【前端升全栈】五分钟了解项目开发的登录部分_第6张图片 进程内存模型

 操作系统会限制一个进程的最大可用内存(大概是 3G),而 session 是存在一个 node.js 进程的,不可能无限储存,一旦存的太多,撑爆 node.js 进程就 GG

【前端升全栈】五分钟了解项目开发的登录部分_第7张图片

 上线的时候每个 node.js 程序都是分多个进程来跑的,因为 单个进程占的内存有限,也浪费其它的内存。而且多核的处 理器可以处理多个进程。这是符合内存与 cpu 的实际情况。 当然进程之间是不能互相访问的,不能共享数据什么的(你 个 QQ 进程总不能去访问我微信进程的数据吧)。回到 node.js,其实每次访问的进程是不一定的(PM2 分配进程, 都挤一个迟早升天),这就导致我在其中一个进程有数据, 到另一个就没有数据了。

【前端升全栈】五分钟了解项目开发的登录部分_第8张图片

 存放内存中意味着读取很快但是也比较昂贵,空间少,一断电就没了(易丢失)

解决方案Redis:

  1. web server最常用的缓存数据库,数据存放在内存中
  2. 相比于mysql,访问速度快(内存和硬盘不是一个数量级的)
  3. 但是成本更高,可存储的数据量更小(内存的硬伤)

 也就是说 redis(内存数据库)和 mysql(硬盘数据库)都是储存数据的;

【前端升全栈】五分钟了解项目开发的登录部分_第9张图片 解决方案Redis

 解决方案Redis:

  • 将web server和redis 拆分为两个单独的服务
  • 双方都是独立的,都是可扩展的(例如都扩展成集群)
  • (包括mysql ,也是一个单独的服务,也可扩展)

 解决问题就是将session存放到redis中就行(之前是存在web server)。而既然分开了,redis 储存多少就不管 node.js 进程的事情了(因为分开了啊,变成两个东西),再说访问,因 为就是这一个 redis,不管谁访问还是访问这一个,也就一份数据,不管是哪个进程访问都给一样的数据。就像是一个独立的仓库。就算是太多了,redis 也可以拆分成集群/机房什么的,不需要我们关心。不考虑丢失数据的意思是重新登录即可。

为何session适合用Redis?

  1. session访问频繁,对性能要求极高
  2. session可不考虑断电丢失数据的问题(内存的硬伤)
  3. session数据量不会太大(相比于mysql 中存储的数据)

 为何网站数据不适合使用Redis?

  • 操作频率不是太高(相比于session操作)
  • 断电不能丢失,必须保留
  • 数据量太大,内存成本太高

8.Redis

安装完成执行 redis-server 就可以启动服务, redis-cli 就可以 起一个主机域名加端口号,使用的话直接 set 键 值即可。get 键可以得到值,也就是 key vlaue 的形式。 keys * 查看所有的
key del 键可以删除一些 key。

【前端升全栈】五分钟了解项目开发的登录部分_第10张图片

【前端升全栈】五分钟了解项目开发的登录部分_第11张图片

【前端升全栈】五分钟了解项目开发的登录部分_第12张图片


9.nodejs链接Redis的demo

用Redis存储session:

  • nodejs连接redis的demo
  • 封装成工具函数,可供API使用

 新建一个 redis-test 的目录,然后 npm 初始化,新建一个 index.js,安装 redis(npm 安装),引入 redis。创建客户端, 需要使用 createClient 方法,传入端口号(默认 6379),主 机名。调用 on 监听 error。接下来就可以通过 set 方法设置 key 了(可以加多一给 redis.print 的属性,完成会打印出来成功的提示)。

get 方法需要传入一个回调函数监听 err 和真正的值(异步), 获取完成就调用 quit 方法退出。Shutdown 就是关闭 redis

const redis =require(" redis")引入redis
const redisclient = redis.createclient(6379,'127.0.0.1')1/创建客户端
redisclient.on( 'error' , err =>{//监听错误
console.log(err)
}
redisclient.set('myname' , 'xiangbei' , redis.print)//设置键值且打印成功提示
redisclient.get('myname' , (err,val)=>{1/获取犍值且获取完成关闭客户端
if (err){
console.log(err)
return
console.log('val:' , val)redisclient.quit()
})

 【前端升全栈】五分钟了解项目开发的登录部分_第13张图片


10.node.js链接Redis封装工具函数 

 接下来就可以把 redis 给封装起来,注意我们回到 blog 重启的话会让我们处于未登录状态,原因也很简单,因为设置session 数据是写在 app.js 中,默认是空对象,这个问题可以通过引入 redis 解决(再重启就是重启 blog 了,与 redis 没有关系,不会丢失登录)。

   打开 db.js ,定义一个 REDIS_CONF 来储存 redis 的配置,同理是分为两种配置。建好配置后我们打开 db 文件夹,建一个 redis.js ,引入 redis 和配置,创建客户端。提供两个函数 set (传入 val key 基本和原本写的一样,只不过需要判断 val 是不是对象,是则利用 stringify 转换)和 get (传入 key ,因为是异步,这里用promise 来封装)。 同样的退出的话也不能写在 get 里面,和 mysql 是一个道理。针对获取的结果其实有多种情况:第一种是瞎传的 key (val=null,即找不到对应 key 的值,这个返回 null )。 第二种是如果传入的是对象我们前面给它 stringify 了,现在
肯定要解开 JSON.parse ,但是如果原本不是对象那自然会报错(用 try catch 捕获进行兼容,并不是错误)直接返回原本的值即可。
REDIS CONF ={
port: 6379,
host: '127.0.8.1'
}
const redis =require( 'redis')
const { REDIS_CONF} = require( '../conf/db')
//创建一个客户端

const redisclient = redis.createclient(REDIS_CONF.portm,REDIS_CONF.host)
redisclient.on( 'error', err =>{
  console.log(err)
))

//定义设置redis的方法
function set (key, val){
   if (typeof val === 'object') {//如果值是对象则转换为json字符串
   val = JSoN.stringify(val)
}
redisClient.set(key, val,redis.print)
}

11.session存入Redis

回到 app.js ,注释掉 session 数据和解析 session 的代码。引 入 get set ,使用 redis 来解析 session 。 如果不存在 userId 则设置状态为 true ,设置 userId ,初始化 session,将 userId 为空对象。那么就可以利用 get 方法来获取 session 。 解析完 session 就可以回到登录来修改,需要引入 set 将其同步到 redis
let needsetcookie = false
let userId = req.cookie.userIdif (l userId){
needsetcookie = true
userId =“s{Date.nok(]${Math.random(1
//设置redis中的session(不要管是userid还是sessionId,反正就是同一个时间戳而已2)set(userId,0)
//i27.0.0.1;6379get i59791251814I_8.7153950171260812“”
获取session
req.sessionId = userId //同一个时网戳换个表达而已,才能让各地使用
get(req.sessionTd) then((sessionData) ={/这里的sessionData就是根操key lblsessionId拿到对应的值《可能找不到即为nutL)if (sessionData -- null) {
lset(req, sessionId,(H)1/似乎没有必要,之前已经设置过时何戢为空对象,但是!!那是因为不存在userId才建的! !req.session - o再把当前session票空(开辟空向)
else (
req.session = sessionbatal
应值给’127.0.0.1:6379 get 1597912518141_0.7153950171260812 "“(1"usernamel" l "qibin ", "reaLnamel"; ["lxe)xaalxe1 xe5l:85luxb国
console.log( sessionbata)return getPostData(req))

12.完成server端登录代码

到目前为止,我们登录和登录验证的功能基本实现,那么就 可以丢到 blog.js 中,而由于需要使用登录和登录验证的路由太多了,我们还是封装成一个中间件比较合适。 定义一个函数 LoginCheck (与 user 里面的作区别),如果找 不到 username 就返回失败 model (主要是来拦截未登录用户)放到路由中,如果该函数有值说明未登录,直接 return 。 无值是已经登录那么执行之前的代码,记得将部分信息替换成真实的登录者(删除和更新路由)。 回到 router 再将登录改成 POST ,注销 login-test。
//登录验证函数
const LoginCheck = (req) => {
if ( req . sessi son. username) {
return Promi se.resolve(
new ErrorModel(”尚未登录”)
}
}
}

 【前端升全栈】五分钟了解项目开发的登录部分_第14张图片


13. 联调-html界面

和前端联调:

  1. 登录功能依赖cookie , 必须用浏览器来联调
  2. cookie跨域不共享的,前端和server端必须同域
  3. 需要用到nignx做代理,让前后端端同域

 有关的 html 已经写好了,比较简单这里直接用就行。安装 http-server,启动 http-serve -p 8001,这个时候启动浏览器,它请求的诸如博客 list 什么的都是以 8001 开头的,自然是找

不到,我们需要做的就是,让 nginx 8001 变为 8000 ,然后统一变成 localhost 8000

14.Nginx配置 

静态服务就是那种不需要服务端解析就能直接拿上来的资源(图片什么的)负载均衡就是让集群的每个机器都尽可能的占用相同的资源(就不要偏差太大,平均流量)。反向代理就是转变访问的地址。
Nginx介绍:
  1. 高性能的web服务器,开源免费
  2. 一般用于做静态服务、 负载均衡(本课用不到)
  3. 还有反向代理(本课用到)

 如下图所示,先经过 nginx,再由 nginx 根据路径(带有 api 的走 8000 node.js,其余的走 8001 html 界面)。

【前端升全栈】五分钟了解项目开发的登录部分_第15张图片 Nginx反向代理

 反向代理就是对客户端不可见(就不看调试里面发送的请求是不知道我的实际地址什么的)的代理(只能由服务端控制),相反的是正向代理,由客户端来控制的代理。

Nginx命令:

  • 测试配置文件格式是否正确nginx -t
  • 启动nginx;重启 nginx -S reload
  • 停止nginx -S stop

 接下来我们就来配置一下 nginx,打开配置文件,可以在第 3 行加上 worker_processes 2;(cpu 有几核就写多少,就可以启动多少个 nginx 进程,性能也就越高,只不过这个一般是运维的人管的,我们不写也行)。 来到 35 行第一个 server,改成 8080 端口,43 行找到 location 这个是我们不用,用#将它们注释掉,写自己的代理,代理分为两个,一个是/即根目录开始的(html)走 8001,一个是 api 开始的(node.js)走 8000(这个还需要把 host 传过去)。

 【前端升全栈】五分钟了解项目开发的登录部分_第16张图片

 保存完成我们利用 nginx -t 测试一下,成功的话则启动 nginx发现可以访问了。

Nginx 出现的问题:
1. nginx: [emerg] unexpected "}" 这个要么是该行附件有特殊字符(如$ 写成了 # 报错)
2. 什么 1133 这个是因为路径有中文。
联调出现的问题:
1. 没有打开 http-server ,也就是说需要同时开启 node.js 个 http-server 静态服务以及 nginx 才可以让 nginx 生效。(所以基本上 nginx 只是充当反向代理的作用就是改改地址,具体 还得靠前两个,因为前两个端口不同, nginx 只是将两个连起来转变其中一个的路径)
2. 页面打开失败,一方面是路由没有 return ,另一方面是路径问题(把相对路径改成绝对路径即可)

你可能感兴趣的:(笔记,Java,全栈,前端,java,开发语言)