浅谈一次登录注册与cookie

2018-02-13  本文已影响0人  mayufo

需求

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

完成效果

代码地址 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 总结

Set-Cookie: <cookie名>=<cookie值>
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

上一篇下一篇

猜你喜欢

热点阅读