美文网首页
浅谈一次登录注册与cookie

浅谈一次登录注册与cookie

作者: mayufo | 来源:发表于2018-02-13 18:21 被阅读0次

    需求

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

    完成效果

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

    相关文章

      网友评论

          本文标题:浅谈一次登录注册与cookie

          本文链接:https://www.haomeiwen.com/subject/hyartftx.html