Khi cài đặt socketio trong nodejs, mặc định mọi client có thể kết nối tới socket server đó. Để xác thực và ủy quyền cho những người dùng có quyền sử dụng kết nối, ta cần cấu hình thêm.

1. Phía server

Phía server cần xác thực xem socket đang kết nối tới có phải là người dùng được xác thực hay không.

Mình sẽ sử dụng JWT để xác thực token gửi từ client lên.

Đầu tiên bạn cần cài đặt thư viện socketio-jwt 

npm install socketio-jwt

Sau đó cấu hình như sau:

require('dotenv').config();
import express from 'express';

var app = express(),
    port = process.env.PORT;
var server = app.listen(port); // return http server
const io = require("socket.io")(server)
const socketioJwt = require('socketio-jwt')

io.on('connection', () => {
  console.log('Socket connected. Authenticating...')
})
  .on('connection', socketioJwt.authorize({
    secret: process.env.ACCESS_TOKEN,
    timeout: 15000
  }))
  .on('authenticated', () => {
    console.log(`Socket authenticated`)
  })

 

Ở đoạn code trên:

  • Khi có kết nối từ client (socket) tới, phía server sẽ log ra dòng Socket connected. Authenticating..., sau đó sử dụng JWT để xác thực access_token gửi từ client lên có đúng hay không.
  • Khi có kết nối từ client, server sẽ đợi trong vòng 15 giây để client gửi access_token, nếu trong vòng 15 giây mà client không gửi lên là kết nối thất bại.
  • Khi client gửi access_token lên và server xác thực thành công, server sẽ log ra dòng Socket authenticated.
  • access_token mà client gửi lên không phải là process.env.ACCESS_TOKEN trong đoạn code trên, mà access_token này được JWT tạo ra dựa trên process.env.ACCESS_TOKEN và gửi về client trước đó rồi. Phía server sẽ dựa vào secret: process.env.ACCESS_TOKEN mà xác thực access_token của client gửi lên xem có trùng khớp không. access_token được server tạo ra trước đó bằng câu lệnh sau:
var jwt = require('jsonwebtoken');

const access_token = jwt.sign({
  username: user.name
}, process.env.ACCESS_TOKEN)

 

2. Phía client

2.1. Client nói chung

Phía client chỉ cần đoạn code sau là được:

// Bạn cần cài npm install socket.io-client trước
import io from 'socket.io-client';
// Giả sử socket server của mình có địa chỉ là http://localhost:9000
var socket = io.connect('http://localhost:9000');

socket.on('connect', function () {
  console.log('socket connected. Authenticating...');
  socket
    .emit('authenticate', {token: localStorage.getItem('accessToken')}) //send the jwt
    .on('authenticated', function () {
      console.log('Socket authenticated')
      //do other things
    })
    .on('unauthorized', function(msg) {
      console.log("unauthorized: " + JSON.stringify(msg.data));
      throw new Error(msg.data.type);
    })
});

 

Ở đoạn code trên:

  • Khi client connect tới socket server, client sẽ log ra dòng socket connected. Authenticating....
  • Khi khởi tạo connect xong, client sẽ gửi một event có tên là authenticate cùng với dữ liệu là access_token được lấy từ localStorage ra (access_token này được bạn lưu vào localStorage trước đó khi nhận nó từ server rồi).
  • Khi server authenticate thành công kết nối của client, server sẽ gửi một event có tên authenticated tới client, ở client sẽ log ra dòng Socket authenticated khi nhận được event đó.
  • Nếu server không xác thực được client đang kết nối tới, server sẽ gửi một event có tên unauthorized tới client, ở client sẽ log ra dòng unauthorized: ${error details}.

 

2.2. Client sử dụng Vuejs

Nếu client của bạn sủ dụng Vuejs và thư viện vue-socket.io:

// main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import VueSocketIO from 'vue-socket.io'

const SocketInstance = new VueSocketIO({
  connection: 'http://localhost:9000'
})
Vue.use(SocketInstance)

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

 

Và trong component nào đó, bạn muốn kết nối và xác thực socket thì khai báo như sau:

// components/Test.vue
<script>
export default {
  name: 'Test',
  components: {
    ...
  },
  mounted: function() {
    ...
  },
  data: () => ({
    ...
  }),
  sockets: {
    connect() {
      console.log('socket connected. Authenticating...');
      this.$socket.emit('authenticate', {token: localStorage.getItem('accessToken')})
    }

    authenticated() {
      console.log('Socket authenticated')
    }

    unauthorized(msg) {
      console.log('Socket unauthorized ' + JSON.stringify(msg.data))
      throw new Error(msg.data.type)
    }

    disconnect() {
      console.log('disconnect');
    }
  }
}
</script>

 

Nếu bạn có nhiều component ở nhiều trang muốn kết nối và xác thực socket thì nên tách các hàm của phần sockets ở trên thành module riêng. Ví dụ:

// helpers/socketUtil.js
export default class SocketUtil {
  static connect() {
    console.log('socket connected. Authenticating...');
    this.$socket.emit('authenticate', {token: localStorage.getItem('accessToken')})
  }

  static authenticated() {
    console.log('Socket authenticated')
  }

  static unauthorized(msg) {
    console.log('Socket unauthorized ' + JSON.stringify(msg.data))
    throw new Error(msg.data.type)
  }

  static disconnect() {
    console.log('disconnect');
  }
}
// components/Test.vue
<script>
import SocketUtil from '../helpers/socketUtil'
const {connect, authenticated, unauthorized, disconnect} = SocketUtil
export default {
  name: 'Test',
  components: {
    ...
  },
  mounted: function() {
    ...
  },
  data: () => ({
    ...
  }),
  sockets: {
    connect,
    authenticated,
    unauthorized,
    disconnect
  }
}
</script>

Và ở một component khác, bạn vẫn có thể tùy biến các function trong moduel SocketUtil trên.

// components/Test2.vue
<script>
import SocketUtil from '../helpers/socketUtil'
const {connect, unauthorized} = SocketUtil
export default {
  name: 'Test2',
  components: {
    ...
  },
  mounted: function() {
    ...
  },
  data: () => ({
    isConnected: false,
    socketMessage: ''
  }),
  sockets: {
    connect,
    authenticated() {
      SocketUtil.authenticated()
      this.isConnected = true
    },
    unauthorized,
    disconnect() {
      SocketUtil.disconnect()
      this.isConnected = false
    },
    eventName (msg) {
      // msg is received from server via event `eventName`
      const timeNow = new Date().toLocaleString()
      this.socketMessage += `\n${timeNow}      ${msg}`
    }
  }
}
</script>

 

Link tham khảo:

https://github.com/auth0-community/socketio-jwt#socketio-jwt

https://github.com/MetinSeylan/Vue-Socket.io