[Node JS] socket.io / jwt authentication / 채팅 회원정보 가져오기

Onam Kwon·2022년 4월 19일
0

Node JS

목록 보기
9/25

  • 기존에 MongoDB를 이용해 회원가입후 로그인을 하면 회원정보를 바탕으로한 채팅 구현.
  • user의 메시지 라고 상단에 나타남
  • 유저 아이디가 다른 클라이언트에게 나타남

▼전체 코드▼

server.js

const express = require('express');
const app = express();
app.use(express.static(__dirname + ''));

const SocketIO = require('socket.io');

// allows you to ejs view engine.
app.set('view engine', 'ejs');  

// importing body-parser to create bodyParser object
const bodyParser = require('body-parser');
// allows you to use req.body var when you use http post method.
app.use(bodyParser.urlencoded({ extended: true }));

// importing .env file
require('dotenv').config();

// Using jsonwebtoken module.
const jwt = require("jsonwebtoken");

const cookieParser = require('cookie-parser');
app.use(cookieParser());

// importing user schema.
const User = require('./module/user');

// importing auth function 
const { auth } = require('./module/authMiddleware');

// importing db function that connects with MongoDB.
const { db } = require('./module/db');

// importing bcrypt moudle to encrypt user password.
const bcrypt = require('bcrypt');

// declaring saltRounds to decide cost factor of salt function.
const saltRounds = 10;

//  To use python script
var PythonShell = require('python-shell');

// MongoDB user info DB
db();

const port = 8080;
const server = app.listen(port, function() {
    console.log('Listening on '+port);
});
const io = SocketIO(server, {path: '/socket.io'});

io
.use((socket, next) => {
    cookieParser()(socket.request, socket.request.res || {}, next);
})
.on('connection', function (socket) {
    const req = socket.request;
    const decoded = jwt.verify(req.cookies.user, process.env.SECRET_KEY);
    socket.name = decoded.docs.id;
    console.log(socket.id, ' connected: ', socket.name);
    
    // broadcasting a entering message to everyone who is in the chatroom
    io.emit('msg', `${socket.name} has entered the chatroom.`);

    // message receives
    socket.on('msg', function (data) {
        console.log(socket.name,': ', data);
        // broadcasting a message to everyone except for the sender
        socket.broadcast.emit('msg', `${socket.name}: ${data}`);
    });

    // user connection lost
    socket.on('disconnect', function (data) {
        io.emit('msg', `${socket.name} has left the chatroom.`);
    });
});

app.get('/chat', auth, function(req, res) {
    const user = req.decoded;
    if(user) {
        const header = user.docs.id + "'s message";
        return res.render('chat', {header:header});
    } else {
        return res.sendFile(__dirname + '/chat.html');
    }
});

chat.ejs

<h1 id="header"><%= header %></h1>
  <!-- importing JS file from socketIO server -->
  <script src="/socket.io/socket.io.js"></script>
  <!-- chat contents will be written down below. -->
  <div id="chatContent">

  </div>
  <input id="myChat" type="text">
  <input type="submit" id="send" value="Send">
  <form id="signOut">
    <button type="button" id="logOut" onclick="signOut()">Log out</button><br><br>
  </form>
                        
                        
<script>
	var socket = io.connect('http://localhost:8080', {
		path: '/socket.io',
		// transports: ['websocket']
    });

    // receiving a message
    socket.on('msg', function (data) {
		var msgLine = $('<div class="msgLine">');
        var msgBox = $('<div class="msgBox">');
        msgBox.append(data);
        msgBox.css('display', 'inline-block');
        msgLine.append(msgBox);
        $('#chatContent').append(msgLine);
        // auto scorll down when a user send something
        chatContent.scrollTop = chatContent.scrollHeight;
    });

    // sending a message
    $("#myChat").on("keyup", function () {
        if (window.event.keyCode==13 && $(this).val()!="") {
            var msgLine = $('<div class="msgLine">');
            var msgBox = $('<div class="msgBox">');
            msgBox.append($(this).val());
            msgBox.css('display', 'inline-block');
            msgLine.css('text-align', 'right');
            msgLine.append(msgBox);
            $('#chatContent').append(msgLine);
            socket.emit('msg', $(this).val());
            $(this).val("");
            chatContent.scrollTop = chatContent.scrollHeight;
        }
    });
    function signOut() {
        $.ajax({
	        type: "get",
	        url: 'http://localhost:8080/logOut',
	        data: {},
	        dataType:'text',
	        success: function(res) {
	        location.reload();
	        }
        });
	}
</script>
<style>
	* {
    	box-sizing: border-box;
	}
    .msgLine {
	    margin: 15px;
    }
    .msgBox {
        border: 1px solid black;
        background: black;
        padding: 2px 5px;
        border-radius: 10px;
    }
    #chatContent {
        border: 1px solid #000;
        width: 100%;
        height: 200px;
        margin-bottom: 10px;
        overflow-y: auto;
    }
    #myChat {
	    width: 100%;
    }
    #msg, #myChat {
    	width: 80%;
	    height: 32px;
	    border-radius: 8px;
    }
    #send {
	    width: 19%;
	    height: 34px;
	    border-radius: 50px;
	    background: black;
	    color: white;
    }
    #logOut {
	    margin-top: 2%;
	    width: 19%;
	    height: 34px;
	    border-radius: 50px;
	    background: black;
	    color: white;
    }
</style>

chat.html

<form id="singin">
	<h1>Sign in is required to enter a chatroom</h1>
	<div class="field">
    	<label for="signinID">ID:</label>
	    <input type="text" id="signinID" name="signinID" placeholder="Enter your fullname" /><br><br>
	</div>
	<div class="field">
    	<label for="signinPW">PW:</label>
	    <input type="text" id="signinPW" name="signinPW" placeholder="Enter your password" /><br><br>
	</div>
	<button type="button" onclick="signInAjax()">Log in</button><br><br>
</form>
<script>
	function signInAjax() {
		const signinID = document.getElementById("signinID").value;
        const signinPW = document.getElementById("signinPW").value;
        document.getElementById("signinID").value = "";
        document.getElementById("signinPW").value = "";
        $.ajax({
        	type: "post",
	        url: 'http://localhost:8080/login/:signInid/:signInpw',
	        data: {id:signinID,pw:signinPW},
	        dataType:'text',
	        success: function(res) {
		        location.reload();
	        }
		});
	}
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

  • 위의 홈 화면에서 Node JS: Messenger로 들어가면 로그인 되어있는 아이디를 기반으로 웹 채팅을 구현했다.
  • 로그인은 저번에 만든 MongoDBJWT를 이용.
  • 아래Node JS: Messenger로 들어가면 /chat경로로 가지며 로그인이 되어있다면 채팅 화면이, 아니라면 로그인화면이 나타난다.

▲비로그인 상태▲

Five유저로 로그인 한 상태▲

▼를 구현하기 위한 server.js/chat경로 코드▼

  • (코드 카피는 최상단 전체코드에서 모듈까지)
app.get('/chat', auth, function(req, res) {
    const user = req.decoded;
    if(user) {
        const header = user.docs.id + "'s message"; //ex) five's message
        return res.render('chat', {header:header});
    } else {
        return res.sendFile(__dirname + '/chat.html');
    }
});

auth함수▼

const jwt = require('jsonwebtoken');
const path = require('path');
// importing .env file
require('dotenv').config({ path: path.resolve(__dirname, '../.env') }); 

exports.auth = (req, res, next) => {
    try {
        // verifying jwt using cookies and secret key then return it to req.decoded
        req.decoded = jwt.verify(req.cookies.user, process.env.SECRET_KEY);
        return next();
    }
    // autorized failed
    catch (error) {
        // Token has been expired
        if (error.name === 'TokenExpiredError') {
            console.log('auth TokenExpiredError');
            next();
            // return res.status(419).json({
            //     code: 419,
            //     message: 'Token has been expired.'
            // }); 
        }
        // JsonWebTokenError
        if (error.name === 'JsonWebTokenError') {
            console.log('JsonWebTokenError');
            next();
            // return res.status(401).json({
            //     code: 401,
            //     message: 'Invalid token.'
            // });
        }
    }
}

server.js내부 socketIO

const port = 8080;
const server = app.listen(port, function() {
    console.log('Listening on '+port);
});

const io = SocketIO(server, {path: '/socket.io'});

io
.use((socket, next) => {
    cookieParser()(socket.request, socket.request.res || {}, next);
})
.on('connection', function (socket) {
    const req = socket.request;
    const decoded = jwt.verify(req.cookies.user, process.env.SECRET_KEY);
    socket.name = decoded.docs.id;
    console.log(socket.id, ' connected: ', socket.name);
    
    // broadcasting a entering message to everyone who is in the chatroom
    io.emit('msg', `${socket.name} has entered the chatroom.`);

    // message receives
    socket.on('msg', function (data) {
        console.log(socket.name,': ', data);
        // broadcasting a message to everyone except for the sender
        socket.broadcast.emit('msg', `${socket.name}: ${data}`);
    });

    // user connection lost
    socket.on('disconnect', function (data) {
        io.emit('msg', `${socket.name} has left the chatroom.`);
    }); 
});

▼클라이언트 chat.ejs

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<h1 id="header"><%= header %></h1>
<!-- importing JS file from socketIO server -->
<script src="/socket.io/socket.io.js"></script>
<!-- chat contents will be written down below. -->
<div id="chatContent">

</div>
<input id="myChat" type="text">
<input type="submit" id="send" value="Send">
<form id="signOut">
  <button type="button" id="logOut" onclick="signOut()">Log out</button><br><br>
</form>
<script>
	var socket = io.connect('http://localhost:8080', {
		path: '/socket.io',
		// transports: ['websocket']
	});

	// receiving a message
	socket.on('msg', function (data) {
		var msgLine = $('<div class="msgLine">');
		var msgBox = $('<div class="msgBox">');
		msgBox.append(data);
		msgBox.css('display', 'inline-block');
		msgLine.append(msgBox);
		chatContent').append(msgLine);
		// auto scorll down when a user send something
		chatContent.scrollTop = chatContent.scrollHeight;
	});

	// sending a message
	$("#myChat").on("keyup", function () {
		if (window.event.keyCode==13 && $(this).val()!="") {
			var msgLine = $('<div class="msgLine">');
			var msgBox = $('<div class="msgBox">');
			msgBox.append($(this).val());
			msgBox.css('display', 'inline-block');
			msgLine.css('text-align', 'right');
			msgLine.append(msgBox);
			$('#chatContent').append(msgLine);
			socket.emit('msg', $(this).val());
			$(this).val("");
			chatContent.scrollTop = chatContent.scrollHeight;
		}
	});
	function signOut() {
		$.ajax({
			type: "get",
				url: 'http://localhost:8080/logOut',
				data: {},
				dataType:'text',
				success: function(res) {
					location.reload();
				}
		});
	}
</script>
<style>
	* {
		box-sizing: border-box;
	}
	.msgLine {
		margin: 15px;
	}
	.msgBox {
		border: 1px solid black;
		background: black;
		padding: 2px 5px;
		border-radius: 10px;
		}
	#chatContent {
		border: 1px solid #000;
		width: 100%;
		height: 200px;
		margin-bottom: 10px;
		overflow-y: auto;
	}
	#myChat {
		width: 100%;
	}
	#msg, #myChat {
		width: 80%;
		height: 32px;
		border-radius: 8px;
	}
	#send {
		width: 19%;
		height: 34px;
		border-radius: 50px;
		background: black;
		color: white;
	}
	#logOut {
		margin-top: 2%;
		width: 19%;
		height: 34px;
		border-radius: 50px;
		background: black;
		color: white;
	}
</style>

▼결과▼

jwt는 브라우저의 쿠키에 저장되어 있으므로 두개의 브라우저를 이용해 twofive로 로그인하여 결과를 확인할 수 있다.

profile
권오남 / Onam Kwon

2개의 댓글

comment-user-thumbnail
2023년 4월 14일

로그인 하면 로그인 화면의 요소들은 display: none; 되는건가요?

1개의 답글