라우팅 설정 

패스워드를 암호화할 모듈과 jwt 모듈을 설치한다.

yarn add bcryptjs @types/bcryptjs
yarn add jsonwebtoken @types/jsonwebtoken

auth router를 설정한다.

src/router/auth.ts

import {Router} from "express";
import {AuthController} from "../controller/AuthController";

const routes = Router();
routes.post('/signin', AuthController.signIn);
routes.post('/signup', AuthController.signUp);

export default routes;

AuthController에 signIn과 signUp 은 프로토타입을 미리 만들어 놓아야 에러가 안생긴다.

router 폴더에 index.ts에 auth를 추가한다.

src/router/index.ts

...

routes.use('/auth', auth);

export default routes;

로그인과 로그아웃 구현

sign up은 이메일과 사용자 이름, 그리고 패스워드 3가지를 입력받아서 사용자 정보를 생성한다. role은 만일 클라이언트에서 지정한 role이 있다면 설정하고 없다면 ROLE_USER를 기본 role로 설정한다.

패스워드는 복호화할 수 없는 비대칭키로 암호화를 해야 하는데 앞에서 bcrypt 모듈을 설치했고 hashSync 함수를 이용해서 복호화 한다.

src/controller/AuthController.ts

export class AuthController {
  static signUp = async (req, res) => {
    const {email, password, username, roles} = req.body;

    const user = new User();
    user.email = email;
    user.password = hashSync(password, 8);
    user.username = username;
    
    // 이메일 중복 체크
    const existUser = await getConnection().getRepository(User)
      .findOne({where: {email}});

    if (existUser) {
      return res.status(400).send({ message: "User Not found." });
    }

    user.roles = [];

    if (roles && roles.length > 0) {
      // where a 혹은 b or 조건 [{ name: 'a'}, {name: 'b'}]
      const res = await getConnection().getRepository(Role).find({
        where: roles.map(name => ({name}))
      })
      user.roles = res;
    } else {
      // 기본 role은 USER
      const res = await getConnection().getRepository(Role).find({
        where: {name: 'ROLE_USER'}
      })
      user.roles = res;
    }

    const result = await getConnection().getRepository(User).save(user);

    res.send(result);
  }
}

이메일과 패스워드를 입력받아서 체크하는 로그인 함수를 구현한다.

해당 사용자 아이디(이메일)이 있는지를 체크한 후 패스워드가 맞는지 체크한다.

패스워드는 복호화가 불가능하므로 compareSync 함수를 사용하여 비교한다.

src/controller/AuthController.ts

export class AuthController {
  static signIn = async (req, res) => {
    const {email, password} = req.body;

    const result = await getConnection().getRepository(User).findOne({where: {email}});

    if (!result) {
      return res.status(400).send({ message: "User Not found." });
    }

    if (!compareSync(password, result.password)) {
      return res.status(400).send({ message: "Invalid password" });
    }

    console.log(result);

    res.send(result);
  }
}  

jwt 토큰 생성

로그인시 jwt 토큰을 생성해서 리턴한다.  토큰 생성시 secret key와 expire 시간을 설정해야 하는데 .env 화일에 생성한다.

.env

secret=11112222
expirationSecondMs=86400000

registered claims인 jti에는 사용자 id를 넣었고 그 밖에 필요한 것들은 private claims 로 정의해서 넣었다. roles는 여러개가 가능하므로 스트링 배열 형태로 넣는다.

  static signIn = async (req, res) => {
    const {email, password} = req.body;

    const user = await getConnection().getRepository(User)
      .findOne({relations: ["roles"], where: {email}});

    if (!user) {
      return res.status(400).send({ message: "User Not found." });
    }

    if (!compareSync(password, user.password)) {
      return res.status(400).send({ message: "Invalid password" });
    }

    // token 생성
    const token = jwt.sign({ jti: user.id, email: user.email, roles: user.roles.map(role => role.name) },
      process.env.secret, {
      subject: user.username,
      algorithm: 'HS512',
      expiresIn: process.env.expirationSecond
    });

    res.send({jwt: token});
  }