0

我想使用 websockets 来跟踪用户最后一次出现的时间,以及他们当前的在线状态(如果他们当前已通过身份验证并使用应用程序)。

我有适用于应用程序某些部分的策略,因此用户将登录然后被引导到适用该策略的概述。

如果用户未通过身份验证,或者如果他们是但会话信息与数据库的信息冲突,则概览将以 notFound 响应。

如果这些简单检查通过,则用户存在,因此应创建两个事件处理程序:

  1. 如果会话过期前没有活动,通知客户端返回认证页面
  2. 当客户端断开连接时,更新他们最后一次出现的时间并改变在线状态

这是策略代码:

const schedule = require('node-schedule')
var sheduledJob

module.exports = async function isSignedIn(req, res, next) {

  // If the user has not signed in, pretend the page does not exist
  if (!req.session.app || !req.session.app.userId) return res.notFound()

  // Store the users id
  let userId = req.session.app.userId

  try {
    // Update the user
    var users = await User.update({
      id: userId
    })
    .set({
      last_accessed: new Date().getTime(),
      online: true
    })
    .meta({
      fetch:true
    })
  } catch (err) {
    // Handle errors
    return res.serverError(err.message)
  }

  // If the user does not exist
  if (!users || users.length !== 1) {
    console.log('policies/isSignedIn: no matching user '+userId)
    // Sign the user out
    delete(req.session.app)
    // Pretend the page does not exist
    return res.notFound()
  }

  // When the user exists
  else {
    // When there is a new client connection
    var io = sails.io
    io.on('connection', socket => {
      console.log("==> Socket Connection Established")
      console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))

      // Cancel an existing job if one exists
      if (sheduledJob) sheduledJob.cancel()
      // Shedule a job to notify the client when the session has expired
      var d = req.session.cookie._expires
      sheduledJob = schedule.scheduleJob(d, () => {
        console.log('The cookie has expired')
        // The client should return to the auth page (will fire disconnecting when doing so)
        req.socket.emit('logout', true)
      })

      // When the client is disconnecting
      socket.on('disconnecting', async reason => {
        console.log('Client disconnecting')
        // As of Sails v1.0.0-46 reason is undefined when the application is lowering
        if (reason) {
          try {
            // Update the user
            var users = await User.update({
              id: userId
            })
            .set({
              last_accessed: new Date().getTime(),
              online: false
            })
          } catch (err) {
            // Handle errors
            return res.serverError(err.message)
          }
        }
      })
    })

    // Proceed as the user exists and we can handle the disconnecting event
    return next()
  }

};

我遇到的问题是,这将在页面最初加载时起作用,但是如果我重新加载概述页面,那么我最终会得到重复的事件处理程序:

加载页面一次(记住会话年龄是 10 秒进行测试):

==> Socket Connection Established
<socketId> <sessionId>
The cookie has expired
Client disconnecting

但是如果我重新加载页面:

==> Socket Connection Established
<socketId> <sessionId>
Client disconnecting
==> Socket Connection Established
<socketId> <sessionId>
==> Socket Connection Established
<socketId> <sessionId>
The cookie has expired
Client disconnecting
Client disconnecting

因此,如果是这种情况,我认为没问题,那么也许我可以为事件侦听器创建一个命名函数,然后最初删除事件侦听器:

const schedule = require('node-schedule')
var sheduledJob
function connectionHandler(req, res, socket) {
  console.log("==> Socket Connection Established")
  console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))
  ...same as before...
}

module.exports = async function isSignedIn(req, res, next) {

 ...

 // When the user exists
 else {
   // When there is a new client connection
   var io = sails.io
   var nsp = io.of('/')
   nsp.removeListener('connection', connectionHandler)
   io.on('connection', socket => connectionHandler(req, res, socket))

   // Proceed as the user exists and we can handle the disconnecting event
   return next()
 }

};

但这会导致相同的重复处理程序,因此我从处理程序中删除了所有 req/res 代码:

function connectionHandler(socket) {
  console.log("==> Socket Connection Established")
  console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))
  socket.on('disconnecting', async reason => {
    console.log('Client disconnecting')
  })
}

并在创建事件时进行了修改:

io.on('connection', connectionHandler)

结果按我的预期工作,在重新加载页面时没有创建任何重复的事件处理程序:

==> Socket Connection Established
<socketId> <sessionId>
Client disconnecting
==> Socket Connection Established
<socketId> <sessionId>

有人可以向我解释我哪里出错了,我真的不明白为什么:

io.on('connection', socket => connectionHandler(req, res, socket))

导致重复的事件处理程序,而:

io.on('connection', connectionHandler)

才不是?

如果有人可以就我在这里出错的地方或如何更好地达到预期结果提供任何建议,那么将不胜感激,非常感谢!

以下是我用来达到这一点的一些参考资料:

  1. https://gist.github.com/mikermcneil/6598661
  2. https://github.com/balderdashy/sails-docs/blob/1.0/concepts/Sessions/sessions.md#when-does-the-sailssid-change
  3. 如何在会话过期时断开套接字
  4. https://stackoverflow.com/a/5422730/2110294
  5. https://github.com/expressjs/session/issues/204#issuecomment-141473499
  6. https://stackoverflow.com/a/33308406/2110294
4

1 回答 1

0

我做了一些更改,似乎已经解决了一些问题:

const schedule = require('node-schedule')
var sheduledJob;

module.exports = async function isSignedIn(req, res, next) {

  // If the user has not signed in, pretend the page does not exist
  if (!req.session.app || !req.session.app.userId) return res.notFound()

  // Store the users id and ip
  let userId = req.session.app.userId
  let userIp = req.headers['x-real-ip']

  try {
    // Update the user
    var users = await User.update({
     id: userId
    })
    .set({
     last_accessed: new Date().getTime(),
     last_accessed_ip: userIp,
     online: true
    })
    .meta({
     fetch:true
    })
  } catch (err) {
    // Handle errors
    return res.serverError(err.message)
  }

  // If the user does not exist
  if (users.length !== 1) {
    console.log('policies/isSignedIn: no matching user '+userId)
    // Sign the user out
    delete(req.session.app)
    // Pretend the page does not exist
    return res.notFound()
  }

  // When the user exists
  else {
    // When there is a new client connection
    var io = sails.io

    io.on('connection', function connectionHandler(socket) {
      // console.log("==> Socket Connection Established")
      // console.log(socket.id, socket.request.headers.cookie.replace('sails.sid=',''))

      // Cancel the existing job if one exists
      if (sheduledJob) sheduledJob.cancel()
      // Shedule a job to notify the client when the session has expired
      var d = req.session.cookie._expires
      sheduledJob = schedule.scheduleJob(d, () => {
        //console.log('The cookie has expired')
        // Sign the user out
        delete req.session.app
        // The client should return to the auth page
        socket.emit('session-expired', 'Your session has expired!')
      })

      // When the client is disconnecting
      socket.on('disconnecting', async function disconnectingHandler(reason) {
        //console.log('Client disconnecting: '+reason)
        // Cancel the existing job if one exists
        if (sheduledJob) sheduledJob.cancel()
        // Remove any event handlers that were created
        io.sockets.removeAllListeners('connection')
        io.sockets.removeAllListeners('disconnecting')
        // As of Sails v1.0.0-46 reason is undefined when the application is lowering
        if (reason) {
          try {
            // Update the user
            var users = await User.update({
              id: userId
            })
            .set({
              last_accessed: new Date().getTime(),
              last_accessed_ip: userIp,
              online: false
            })
          } catch (err) {
            // Handle errors
            return res.serverError(err.message)
          }
        }
      })
    })

    // Proceed as the user exists and we can handle the disconnecting event
    return next()
  }

};

我认为这里的主要问题是我没有正确删除事件侦听器,这需要在断开连接事件被触发时发生。

根据https://github.com/expressjs/session#rolling,会话需要添加rolling: true到 config.sessions 以在每个请求上刷新,到期日期由 config.session.cookie.maxAge 确定。

于 2018-02-27T21:17:04.853 回答