2장 localLogin 구현

그렇다면 실제로 이걸 어떻게 이용하여 localLogin을 하는지 알아 보도록 하겠습니다.

일단 local부분만 간단히 생각해 본다면

  1. ID,Password을 받으면
  2. response로 token을 넘겨주고 그걸 localStorage에 저장합니다.
  3. 그 후에 만약 서버에 요청을 할 일이 생긴다면 header에 token을 넣어서 보내주면
  4. 서버는 token을 분석하여 현재 유저를 확인
  5. 알맞은 요청이라면 제대로된 값을 보내줍니다.

대략 이정도 인데요. 일단 모두가 알기쉽게 jquery로 하겠습니다.

일단 express login 정도로 express 폴더를 생성합니다.

그 후에 우리는 mongoose을 사용할 예정이니 mongoose을 추가 npm install mongoose mongoose에 대한 설명은 검색하시면 많이 나옵니다. 간단하게 mongoDB을 쉽게 사용하기위한 라이브러리라고 하죠.

그 다음 루트폴더에 model폴더를 만들고 user.js을 만듭니다. 여기에 user의 모델을 정의하겠습니다.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
    email: String,
    localPassword: String,
    fbToken: String,
    jsonWebToken: String 
});

module.exports = mongoose.model('user',userSchema);

fbToken은 그 다음 페이스북 로그인을 위해서 만들어놓았습니다.처음 만들때 순서가 약간 뒤죽박죽... 그 다음엔 routes 폴더에 api.js을 추가합니다.

var express = require('express');
var router = express.Router();

var users = require('./../model/user');
var jwt = require('jsonwebtoken');
var jwtSecret = 'secret';
/***********************************
 *           Local Login           *
 ***********************************/
router.post('/login/local', function (req, res, next) {
   var localEmail = req.body.email;
   var localPassword = req.body.password;

   var findConditionLocalUser = {
       email: localEmail,
       localPassword: localPassword
   }
   users.findOne(findConditionLocalUser)
        .exec(function (err, user) {
            if (err){
                res.json({
                    type: false,
                    data: "Error occured " + err
                });
            } else if (!user) {
                res.json({
                    type: false,
                    data: "Incorrect email/password"
                });
            } else if(user) {
                res.json({
                    type: true,
                    data: user,
                    token: user.jsonWebToken
                });
            }
        });
});

router.post('/login/local/signup', function (req, res, next) {
    var localEmail = req.body.email;
    var localPassword = req.body.password;
    console.log(localEmail);
    console.log(localPassword);

    var findConditionLocalUser = {
       email: localEmail
    }

    users.findOne(findConditionLocalUser)
        .exec(function (err, user) {
            if (err){
                res.json({
                    type: false,
                    data: "Error occured " + err
                });
            } else if (user) {
                res.json({
                    type: false,
                    data: "Email already exists"
                });
            } else if(!user) {
                localSignup(localEmail, localPassword, function (err, savedUser) {
                   if (err){
                        res.json({
                            type: false,
                            data: "Error occured " + err
                        });
                    } else {
                        res.json({
                            type: true,
                            data: savedUser,
                            token: savedUser.jsonWebToken
                        });
                    } 
                });
            }
        })
});

function localSignup(userId, userPassword, next) {
    var userModel = new users();
    userModel.email = userId;
    userModel.localPassword = userPassword;
    console.log(userModel);
    userModel.save(function (err, newUser) {
        newUser.jsonWebToken = jwt.sign(newUser, jwtSecret);
        newUser.save(function (err, savedUser) {
            next(err, savedUser);
        });
    });
}

router.get('/me', ensureAuthorized, function (req, res, next) {
    var findConditionToken = {
        jsonWebToken: req.token
    };
    console.log(req.token);
    users.findOne(findConditionToken, function (err, user) {
        if (err){
            res.json({
                type: false,
                data: "Error occured: " + err
            });
        } else {
            console.log("me : " + user);
            res.json({
                type: true,
                data: user
            });
        }
    })
});

function ensureAuthorized(req, res, next) {
    var bearerToken;
    var bearerHeader = req.headers["authorization"];
    console.log(bearerHeader);
    if (typeof bearerHeader !== "undefined") {
        var bearer = bearerHeader.split(" ");
        bearerToken = bearer[1];
        req.token = bearerToken;
        next();
    } else {
        res.send(403);
    }
}

하면 됩니다. 대략 설명을 하면 /login/local으로 post요청이 들어오면 email,password을 parameter로 받고 검색, 일치하면 로그인, 일치하지 않으면 err발생. /login/local/signup으로 요청이 들어오면 똑같이 하되, 없다면 새로 만들어 줍니다. Signup부분은 따로 함수로 만들어 주었으며 callback함수를 가지고 있습니다. 그리고 user을 생성하면 그즉시 token을 만들어 주고 저장합니다. 그 뒤 token을 이용하여 통신을 합니다.

그리고 /me로 get 요청이 들어올 경우에는 일단 ensureAuthorized 함수를 거칩니다. ensureAuthorized 함수는 authorization을 분석하여 token을 추출합니다. 그 뒤에 token을 이용해 알맞은 user을 찾고 데이터를 돌려줍니다. 이부분에 대한 설명은 Node.js 로 JWT(JsonWebToken) 방식 RESTful api login 구현 을 보시면 좀 더 자세하게 나와있습니다.

여기서 보면 /me의 요청에는 user_id나 session등으로 user을 검사하지 않고 token으로만 검사합니다.

그리고 routes/index.js

router.get('/', function(req, res, next) {
  res.sendfile('index.html');
});

로 바꾸어 줍니다. 이제 app.js 부분에 우리가 만든 부분을 추가만 시켜주면 됩니다.

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/token-login-social');

윗부분에 적당히 추가하고

var api = require('./routes/api');
app.use('/api', api);

을 적당하게 추가하면 완료입니다.

이제 backend는 완료입니다. frontend을 수정하여 봅시다.

<!DOCTYPE html>
<html>
<head>
<title>Facebook Login JavaScript Example</title>
<meta charset="UTF-8">
<script type="text/javascript" src="javascripts/jquery.min.js"></script>
<script type="text/javascript" src="javascripts/localLogin.js"></script>
<script type="text/javascript" src="javascripts/tokenApi.js"></script>
</head>
<body>


loginForm
<div id="localLoginForm">
    Email: <input type="text" name="email" class="email"><br>
    Password: <input type="text" name="password" class="password"><br>
    <button onclick="localLogin()">Login</button>
</div>


SignupForm
<div id="localSignupForm">
    Email: <input type="text" name="email" class="email"><br>
    Password: <input type="text" name="password" class="password"><br>
    <button onclick="localSignup()">Signup</button>
</div>

get user Email
<button onclick="getEmail()">get Email</button>
<button onclick="cleanEmail()">Email Clean</button>
<div id="getEmail">
</div>

<button onclick="logout()">logout</button>
</body>
</html>

을 추가. localLogin.js

function localLogin() {
    var localEmail = $("#localLoginForm .email").val();
    var localPassword = $("#localLoginForm .password").val();
    console.log(localEmail);
    console.log(localPassword);
    var postUserInformation = {
        email: localEmail,
        password: localPassword
    };
    $.post('/api/login/local', postUserInformation, 
        function(response){
            console.log(response);
            if (response.type == true){
                localStorage.setItem("token", response.token);
            }
         });
    $("#localLoginForm .email").val("");
    $("#localLoginForm .password").val("");
}

function localSignup() {
    var localEmail = $("#localSignupForm .email").val();
    var localPassword = $("#localSignupForm .password").val();
    console.log(localEmail);
    console.log(localPassword);
    var postUserInformation = {
        email: localEmail,
        password: localPassword
    };
    $.post('/api/login/local/signup', postUserInformation, 
        function(response){
            console.log(response);
            if (response.type == true){
                localStorage.setItem("token", response.token);
            }
         });
    $("#localSignupForm .email").val("");
    $("#localSignupForm .password").val("");
}

잘 로그인이 되었다면 localStorage에 token을 저장합니다. tokenApi.js

function getEmail() {
    var token = localStorage.getItem("token");
    $.ajax({
        type: "GET",
        url: "/api/me",
        beforeSend: function (xhr) {
            xhr.setRequestHeader("Authorization","Bearer " + token);
        },
        success: function (res) {
            console.log(res);
            document.getElementById('getEmail').innerHTML = res.data.email;
        }
    });
}
function clenaEmail() {
    document.getElementById('getEmail').innerHTML = ''; 
}
function logout() {
    localStorage.removeItem("token");
}

을 하시면 완료입니다. 이부분에서보시면 /api/me에 요청하기 전에 Header에 token을 추가하는 것을 볼 수 있습니다.

이러면 완료입니다. 실제로 구동한다면

일단 signup을 하면

결과로 token을 받고

실제 DB에는 jsonWebToken과 localPassword, email이 저장되었습니다.(이번에는 저장이 된다는걸 보여주기 위해 password을 암호화하지 않았지만 무조건 암호화 해야합니다)

크롬 개발자 도구를 통해 보면 token이 잘 저장되어있는것을 볼 수 있습니다. 로그아웃을 하면 token 이 사라집니다. 이걸 통해 우리는 현재 token이 있다면 로그인이 된 상태, token이 없다면 로그인이 안된 상태라고 생각할 수 있습니다.

대략 이런식으로 local 로그인을 구현할 수 있습니다. 혹시 이상하거나 잘 모르시는 부분이 있다면 댓글로 말씀해주시면 바로 수정하겠습니다.