Web/javascript

Node.js 회원가입, 로그인 적용시키기

dustKim 2024. 5. 7. 01:48
auth.js 파일을 추가하여 로그인, 회원가입(회원정보 확인, 아이디를 보내면 해당 객체의 정보만 출력)을 적용해 보기

 

서버를 실행시킬 파일
더보기
import express from "express";
import morgan from "morgan";
import tweetsRouter from "./router/tweets.js";
import authRouter from "./router/auth.js";

const app = express();

app.use(express.json());
app.use(morgan("dev"));

app.use("/tweets", tweetsRouter);
app.use("/auth", authRouter);

app.use((req, res, next) => {
  res.sendStatus(404);
});

app.listen(8080);

 

/auth 경로로 라우터를 만들어둔다.

 

정보를 처리하는 파일
더보기
import express from "express";
import { body } from "express-validator";
import * as authController from "../controller/auth.js";
import { validate } from "../middleware/validator.js";

const router = express.Router();

const validateSignup = [
  body("username")
    .trim()
    .isLength({ min: 3 })
    .withMessage("최소 3자 이상 입력"),
  body("password")
    .trim()
    .isLength({ min: 4 })
    .withMessage("최소 4자 이상 입력"),
  body("email").trim().isEmail().withMessage("이메일 형식 확인"),
  validate,
];

router.post("/signup", validateSignup, authController.signup);

router.post("/login", authController.login);

export default router;

 

필요한 모듈을 import 한다.

router 객체에 express를 적용시키고 Router를 사용할 수 있게 만든다.

 

그 후에 post로 정보를 받고 처리할 수 있게 만든다. 중간에 validateSignup은 조건을 걸어준 것이다.

 

controller 폴더에 auth.js파일

더보기
import * as authRepository from "../data/auth.js";

export async function signup(req, res, next) {
  const { username, password, name, email } = req.body;
  const users = await authRepository.createUser(
    username,
    password,
    name,
    email
  );
  if (users) {
    res.status(201).json(users);
  }
}

export async function login(req, res, next) {
  const { username, password } = req.body;
  const user = await authRepository.login(username);
  if (user) {
    res.status(201).json(`${username} 로그인 완료`);
  } else {
    res
      .status(404)
      .json({ message: `${username} 님 아이디 또는 비밀번호를 확인해주세요` });
  }
}

 

data 폴더에 auth.js 파일에 함수를 호출할 수 있도록 import 하고, signup, login 함수를 생성한다.

 

회원가입의 경우 body에서 username, password, name, email을 받아서 data.js의 createUser함수를 호출한다.

data.js에서 데이터를 받아서 전개구문 (...)연산자를 사용하여 기존의 객체 안에 보내준 데이터에 관한 배열을 추가해 준다.

 

로그인의 경우 body에서 username, password를 받아 data.js의 login 함수를 호출한다. 

dats.js에서 데이터를 받아서 기존 정보를 갖고 있는 객체 안에서 username과 같은 배열을 반환한다.

조건문을 사용하여 로그인에 성공하거나 메세지를 보내 다시 확인하라 알려준다.

 

기존 데이터를 갖고 있는 파일
더보기
let users = [
  {
    id: "1",
    userid: "apple",
    password: "1111",
    name: "김사과",
    email: "apple@apple.com",
    url: "https://www.logoyogo.com/web/wp-content/uploads/edd/2021/02/logoyogo-1-45.jpg",
  },
  {
    id: "2",
    userid: "banana",
    password: "2222",
    name: "반하나",
    email: "banana@banana.com",
    url: "https://img.freepik.com/premium-vector/banana-cute-kawaii-style-fruit-character-vector-illustration_787461-1772.jpg",
  },
];

export async function createUser(username, password, name, email) {
  const user = {
    id: "10",
    username,
    password,
    name,
    email,
    url: "https://www.logoyogo.com/web/wp-content/uploads/edd/2021/02/logoyogo-1-45.jpg",
  };
  users = [user, ...users];
  return users;
}

export async function login(username) {
  return users.find((users) => users.username === username);
}

 

기존 데이터를 갖고 있는 data.js파일은 전달받은 데이터를 기반으로 기존에 있던 데이터와 비교해 주면 된다.

 

createUser, 회원가입의 경우 새로운 user라는 객체에 전달받은 데이터를 담고 전개구문의 (...)연산자를 사용하여 추가해 준다.

 

login, 로그인의 경우 전달받은 username을 기준으로 기존에 있는 users 객체 안에서 순회하며 같은 username을 가진 배열을 반환한다.

 

결과
더보기
회원가입 결과

 

로그인 결과

 


 

Authentication(인증)

 

session & cookie

 

1.쿠키

- 클라이언트 컴퓨터에 저장된 작은 데이터 조각

- 서버로부터 전송되어 클라이언트 웹브라우저에 저장

- 텍스트 형식으로 주로 사용자 인증, 설정, 장바구니 등에 사용

 

2. 세선

- 웹 서버 측에서 유지되는 상태 정보

- 사용자에 대한 고유한 세선아이디를 통해 식별

- 서버 메모리 또는 데이터베이스에 저장할 수 있다.

 

 

JWT(JSON Web Token)

 

- 웹 애플리케이션과 서비스 간에 정보를 안전하게 전달하기 위한 인증 및 권한 메커니즘을 구현하는 데 사용되는 표준화된 방법 중 하나이다.

- JSON 포맷을 사용하여 정보를 표현하고 서명 및 암호화를 통해 정보를 무결성 보장한다.

- {Header | Payload | Signature}

   Header: 토큰 유형 및 서명 알고리즘과 같은 메타데이터가 포함된다.

   Payload: 토큰에 포함될 데이터가 들어있는 부분이다.

   Signature: 헤더, 페이로드 및 비밀 키를 사용하여 생성된 서명으로 토큰의 무결성을 검증하는 데 사용한다.

더보기
import jwt from 'jsonwebtoken';

const secret = 'abcdefg1234%^&*';
const token = jwt.sign(
    {
        id: 'apple',
        isAdmin: false 
    },
    secret,
    { expiresIn: 2 } 
)
console.log(secret);

setTimeout(() => {
    jwt.verify(token, secret, (error, decoded) => {
        console.log(error, decoded);
    });
}, 1000); // expiresIn 시간보다 적어야 나옴. 만약 크면 사라져서 나오지 않는다.
console.log(token);

 

결과

 

토큰을 생성하는데 expiresIn이 토큰이 유지되는 시간이다. setTimeout은 시간을 정해주면 그 시간만큼 기다리고 실행이 되는데 여기서 토큰의 유지 시간보다 기다리는 시간이 길어지면 에러가 나오게 된다.

 

위에서 setTimeout에서 JWT의 verify 메서드를 사용하여 토큰을 검증하고, 검증 결과를 출력하는데, 첫 번째로 검증할 token, 두 번째는 비밀키인 secret을 전달하여 검증이 완료되면 error와 decode를 인자로 받는 콜백함수를 호출한다.

에러가 없으면 error는 null을 전달하고, decode는 검증된 토큰의 내용이 객체 형태로 전달된다.

 

여기서 setTimeout의 시간을 토큰 유지 시간보다 길게 하면 error를 전달하고, decode는 undefined를 전달한다.

 

bycrypt

 

- 해시 함수를 사용하여 비밀번호를 안전하게 저장하는 데 사용되는 암호화 라이브러리이다.
- 단방향 해시 함수로 한 번 해시된 값을 다시 원래 값으로 역추적하는 것은 불가능하다.
- 솔트(salt): 해시에 고유한 솔트 값을 추가하여 보안성을 높임. 같은 비밀번호를 가진 사용자가 있더라도 서로 다른 해시값을 가진다.
- 작업인자(Adaptive Work Factor): 매개변수를 조정하여 해시 작업의 복잡성을 조절. 암호 분석학적으로 안전한 해시 함수를 유지하면서도 암호화 작업에 필요한 시간을 조절할 수 있게 한다.

 

✔ 해시 함수
임의의 길이의 데이터를 받아서 고정된 길이의 고유한 값으로 변환하는 함수. 이러한 변환된 값은 해시 값 또는 해시 코드라고 한다.
- 동일한 입력에 대해 항상 동일한 해시 값을 생성한다.
    예)
    1234 -> asdfihlkj12
    1234 -> asdfihlkj12
- 고정된 출력 길이를 생성 
- 해시된 값을 통해 원본 값을 복수할 수 없다.
    예)
    1234 + 10 -> 1111aaaabbbb
    1234 + 5 -> 1111aaaacccc

더보기
import * as bcrypt from 'bcrypt';
// npm i bcrypt

const password = 'abcd1234';
const hashed = bcrypt.hashSync(password, 10);
console.log(`password: ${password}, hashed: ${hashed}`);


// abcd1234
// $2b$10$8BveypaYUdyY1m/C5ls.UeDSDfft6vNalPsVIt3TdpTIbkqFMopq.

const result = bcrypt.compareSync('abcd1234', hashed);
console.log(result);

 

결과

 

위에서 hashSync를 사용하여 password를 임의의 고정된 길이 10을 받아서 고유한 값으로 변환시킨다.

그리고 comparaSync를 사용하여 기존 값과 변화된 값을 비교하여 true, false를 반환받을 수 있다.

하지만 해시된 값을 원본 값으로 복원할 수는 없다.

 

jsonWebtoken

 

- 웹 애플리케이션 인증 및 정보 교환을 위한 토큰 기반의 인증 방식 중 하나이다.

- Base64 인코딩 된 JSON 객체이며, 사용자 정보 및 기타 데이터를 안전하게 전송하기 위해 사용된다.

- header: JWT의 유형과 핵심 알고리즘이 포함된다.

    {
        "alg" : "HS256",
        "typ" : "JWT"
    }

- Payload: 토큰에 담길 정보가 포함된다.
    {   
        id: 'apple',
        isAdmin: false
    }
- Signature: 헤더와 페이로드를 인코딩하고 비밀 키를 사용하여 서명된 문자열. 서명은 토큰이 변조되지 않았음을 확인하는 데 사용한다.

 

- sign()

    jsonwebtoken.sing(payload, secretOrPrivateKey, [option, callback])
    payload: JWT에 포함될 페이로드 데이터
    secretOrPrivateKey: JWT 서명하기 위해 사용될 비밀 키 또는 개인 키