浅谈一次登录注册与cookie

需求

我希望用户能够注册 ->注册后跳转到登录页面 -> 登录页面跳转以后跳转到首页显示我的登录密码

完成效果

代码地址 https://github.com/mayufo/jquery-demo/tree/master/cookies
运行 node server 7000

注册页面

  1. 前端页面
注册页面
<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>
  1. 后端页面返回index
    server.js用来模拟后端的返回
if(path === '/'){  
        let string = fs.readFileSync('./index.html', 'utf8')
        response.statusCode = 200
        response.setHeader('Content-Type', 'text/html;charset=utf-8')
        response.write(string)
        response.end()
    }
  1. 前端注册逻辑

包装数据: 拿到input框中用户输入的所有数据,存在hash这个对象中

let $signUp = $('#signUpForm')

$signUp.on('submit', (e) => { 
   let hash = {}
   let need = ['email', 'password', 'password_confirmation'] // 需要包装成对象的数据
   need.forEach((name) => {
        hash[name] = $signUp.find(`[name=${name}]`).val()  // 用户必填数据包装到hash中
    })
}

需要校验

  1. 邮箱、密码和确认密码的不能为空
  2. 密码和确认密码匹配

每次校验前,先清空上次校验的内容

$signUp.find('.error').each((index, span) => {
        $(span).text('') 
    })

校验

if (hash['email'] === '') {
        $signUp.find('[name="email"]').siblings('.error').text('填邮箱啊')
    } else if (hash['password'] === '') {
        $signUp.find('[name="password"]').siblings('.error').text('填密码啊')
    } else if (hash['password_confirmation'] === '') {
        $signUp.find('[name="password_confirmation"]').siblings('.error').text('填确认密码啊')
    } else if (hash['password'] !== hash['password_confirmation']) {
        $signUp.find('[name="password_confirmation"]').siblings('.error').text('密码不匹配')
    }
  1. 前端通过校验后发请求

如果以上校验都能通过,就发post 请求,请求成果去到登录页面,如果没有成功,报错

$.post('/signUp', hash)
            .then((response) => {
                window.location.href= '/signIn'  // 请求成功,跳转到登录页面
            }, (request) => {
                alert('邮箱与密码不匹配')
            })
  1. 后端拿到前端的注册信息, 一段一段,需要封装方法,等全部拿完,再进行操作

如果前端给的注册信息很多,那么它的传输是一段一段的,需要封装一下,当所有的数据都拿到以后,进行存储

// 封装拿数据
function readBody (request) { 
  return new Promise((resolve, reject) => {
    let body = []
    request.on('data', (chunk) => { 
      body.push(chunk)
    }).on('end', () => {
      body = Buffer.concat(body).toString();
      resolve(body)
    })
   })
}
  1. 注册的后端逻辑
if (path === '/signUp' && method === 'POST') {
        // 注册
        readBody(request).then((body) => {
// body拿到的数据 email=1w%401&password=1&password_confirmation=1
        let string = body.split('&')   //  以&为例拆分为数组, 每个string ['email=1w%401', 'password=1', 'password_confirmation=1']
        let hash = {}
        string.forEach((string) => { 
          let parse = string.split('=')  // 以等号为例拆分为数组, 分别取key和value
          let key = parse[0]
          let value = parse[1]
// 可以看到body的@符号已经编码成了%40,需要解码
          hash[key] = decodeURIComponent(value) 
        })
        let {email, password, password_confirmation} = hash
        if (email.indexOf('@') === -1) {  // 存储的时候判断email的合法性
            response.statusCode = 400
            // response.write('email is bad ')
            response.write(`{
                "email": "invalid" 
            }`)
        } else if (password !== password_confirmation ) {  // 判断两次密码输入时候一致
            response.statusCode = 400
            response.write('password not match')
        } else {
            var users = fs.readFileSync('./user.json', 'utf8')  // 读取存储数据的文件
            try {
                users = JSON.parse(users) // 第一次拿出来的时候是[object Object]
            } catch (exception) {
                users = []  // 如果没有拿到值,默认为[]
            }
            let isUse = false  // 判断密码是否被注册过
            for (let i = 0; i < users.length; i++) {
                let user = users[i]
                if(user.email === email) {
                    isUse = true
                    break
                }
            }
            // 如果密码已经注册过了
            if (isUse) {
                response.statusCode = 400
                response.write('Email is User')
            } else {
              // 将数据存到刚刚从文件拿出的数组中,并在此存入文件中
                users.push({email: email, password: password, password_confirmation: password_confirmation})
                fs.writeFileSync('./user.json', JSON.stringify(users))
                response.statusCode = 200
            }
        }
        response.end()
      })
    }
  1. 通过验证后,后端返回200,前端跳转到登录页面

登录页面

  1. 后端渲染登录页面
if (path === '/signIn' && method === 'GET') {
      let string = fs.readFileSync('./signIn.html', 'utf8')
      response.statusCode = 200
      response.setHeader('Content-Type', 'text/html;charset=utf-8')
      response.write(string)
      response.end()
    }
  1. 登录页面样式
登录页面
<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>
  1. 当用户点击登录后
let $signIn = $('#signInForm')

$signIn.on('submit', (e) => {
    e.preventDefault()
    let hash = {}
    let need = ['email', 'password']
// 将登录的数据包装成对象
    need.forEach((name) => {
        hash[name] = $signIn.find(`[name=${name}]`).val()
    })
// 每次请求前,先清除上次的错误提示
    $signIn.find('.error').each((index, span) => {
        $(span).text('')
    })
// 校验输入项
    if (hash.email === '') {
        $signIn.find('[name="email"]').siblings('.error').text('填邮箱啊')
    } else if (hash['password'] === '') {
        $signIn.find('[name="password"]').siblings('.error').text('填密码啊')
    } else {
// 发送请求
        $.post('/signUp', hash)
            .then((response) => {
              window.location.href= '/enter'              // 请求成功进入enter页面
            }, (request) => {
 // 如果后端返回json数据,并且后端设置了请求头response.setHeader('Content-Type', 'application/json;charset=utf-8')
                let {error} = request.responseJSON 
                if (error.email && error.email === 'invalid') {
                    console.log('你的邮箱输入错误')
                    $signIn.find('[name="email"]').siblings('.error').text('邮箱格式错误')
                }
            })
    }
})

  1. 登录后后端的处理逻辑

为了区别登录的用户和没有登录的用户,可以当用户登录校验成功以后,给头部写上cookie, response.setHeader('Set-Cookie', sign_in_email=${email}) 以此区分

if (path === '/signIn' && method === 'POST') {
    // 登录
    readBody(request).then(body => {
      let string = body.split('&')
      let hash = {}
// 对拿到的数据解析为对象
      string.forEach(string => {
        let parse = string.split('=')
        let key = parse[0]
        let value = parse[1]
        hash[key] = decodeURIComponent(value)
      })
      let { email, password } = hash
// 读取存储本地数据的文件
      var users = fs.readFileSync('./user.json', 'utf8')
      try {
        users = JSON.parse(users)
      } catch (exception) {
        users = []  // 当为空值的时候,附默认值
      }
      if (hash.email.indexOf('@') === -1) {   // 判断输入的email是否合理
        response.statusCode = 401
        response.setHeader('Content-Type', 'application/json;charset=utf-8')
        response.write(`{
        "errors": {
            "email": "invalid"
        }}`)
      } else {
        let found  // 判断是否能找到对应的存储
        for (let i = 0; i < users.length; i++) {
          if (users[i].email === email && users[i].password === password) {
            found = true
          }
        }
        if (found) {  // 如果本地数据库没有存储,报错
          response.statusCode = 200
          response.setHeader('Set-Cookie', `sign_in_email=${email}`)  // 当登录成功后,设置cookie
        } else {
          response.statusCode = 401 // 认证失败
        }
      }
      response.end()
    })
  }

登录页面后,进入enter页面

  1. 登录后的页面
登录后页面
  1. 前端页面
<body>
  <h1>你的密码是: __password__</h1>
</body>
  1. 后端渲染登录的页面

if (path === '/enter') {
// 渲染页面
    let string = fs.readFileSync('./enter.html', 'utf8')
    response.statusCode = 200
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    if (request.headers.cookie) {
// 当存在多个cookie的时候,分割为数组,将其包装成对象
      let cookies = request.headers.cookie.split('; ')
      let hash = {}
      for (let i = 0; i < cookies.length; i++) {
        let parts = cookies[i].split('=')
        let key = parts[0]
        let value = parts[1]
        hash[key] = value
      }
      let email = hash.sign_in_email // 筛选出email
      let users = fs.readFileSync('./user.json', 'utf-8')
      users = JSON.parse(users)

      let foundUser
      for (let i = 0; i < users.length; i++) {
        if (users[i].email === email) {  // 将cookie中的email 和本地的email进行对比,如果存在,标志founUser, 并替换__password__为密码
          foundUser = users[i]
          break
        }
      }
      if (foundUser) {
        string = string.replace('__password__', foundUser.password)
      } else {
        string = string.replace('__password__', '不知道')
      }
      response.write(string)  
      response.end()
    }
  }

cookie 总结

  • 设置cookie, 所有同源的请求都会带cookie

  • 当服务器收到HTTP请求时,服务器可以在响应头里面添加一个cookie

Set-Cookie: <cookie名>=<cookie值>
  • 和关闭浏览器便失效的会话期Cookie不同,持久性Cookie可以指定一个特定的过期时间

  • 安全的Cookie只应通过HTTPS协议加密过的请求发送给服务端。即便设置了 Secure 标记,只能使用https

Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>   // cookie 的最长有效时间
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit> // 在 cookie 失效之前需要经过的秒数
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>  // 指定 cookie 可以送达的主机名。针对的域名,一个网站只会带上自己域名的cookies
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure // 必须使用https 才能访问cookies
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly // js不能访问cookies,但是用户可以手动改,但是js不能读取到

document.cookie  // 读取cookie

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 最近在写前端关于登录与注册相关的小Demo,觉得登录注册中的一些细节是值得我去关注并且去不断总结的,因为只有这样,...
    井润阅读 5,468评论 0 0
  • 概述 我们每天在使用电脑的时候都会去注册或者登录,作为前端是必须要了解其中的过程的。 注册 后端 后端需要一个路由...
    bowen_wu阅读 6,052评论 0 1
  • 首先,写一个简单的注册页面sign_up.html(前端) 把用户输入的内容放到一个哈希中(前端) 给server...
    是刘快啊阅读 6,251评论 0 5
  • 流程 用户打开sign_up注册,向服务器发送post请求,发送email,password,password_c...
    壹如既往的活著阅读 8,792评论 0 1
  • 需求 我希望用户能够注册 ->注册后跳转到登录页面 -> 登录页面跳转以后跳转到首页显示我的登录密码 注册页面 前...
    独钓寒江月阅读 3,624评论 0 0

友情链接更多精彩内容