Tôi chẳng khi nào nghĩ đến tương lai. Nó sẽ đến nhanh.
Albert Einstein
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_TOKENtrong đoạn code trên, mà access_token này được JWT tạo ra dựa trênprocess.env.ACCESS_TOKENvà gửi về client trước đó rồi. Phía server sẽ dựa vàosecret: process.env.ACCESS_TOKENmà 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à
authenticatecù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
authenticatedtới client, ở client sẽ log ra dòngSocket authenticatedkhi 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
unauthorizedtới client, ở client sẽ log ra dòngunauthorized: ${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
