2017-10-26

AdonisJS 4.0 Tutorial

본 튜토리얼에서는 대부분의 사이트에 기본적으로 필요한 기능을 익히는데 목적이 있습니다. 예제에서는 관리자로 로그인/로그아웃하고, 관리자를 등록/수정/삭제 등을 할 수 있는 기본적인 기능을 포함하는 예제를 만들어봅니다. 그리고, 비밀번호의 단방향 암호화(SHA-256)를 해보고, 개인정보(이름,전화번호,이메일 등)을 AES-256 로 암호화하여 DBMS에 저장/관리하는 기능도 포함됩니다.

  1. 시작
  2. 첫페이지 만들어보기
  3. 모델 생성
  4. 관리자 목록 보여주기
  5. 관리자 추가하기
  6. 관리자 비밀번호 변경하기
  7. 관리자 수정하기
  8. 관리자 삭제하기
  9. 비밀번호 단방향암호화(SHA256) 하기
  10. 개인정보 양방향암호화(AES256) 하기
  11. 로그인/로그아웃 처리하기

2017-10-25

(AdonisJs 4.0 Tutorial) 로그인/로그아웃 처리하기


  • 명색이 관리자 화면인데, 아무나 들어와서 조작하면 안되겠지요? 이제는 마지막으로 로그인/로그아웃 처리를 해보겠습니다. 로그인/로그아웃은 세션을 이용해서 구현합니다.
  • 로그인 화면을 구성합니다. xyz/resources/views/mgmt/index.edge 을 작성합니다.
    <!DOCTYPE html>
    <html lang="ko">
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    </head>
    <body>
     
    <div class="container" style="margin-top: 20px">
      <form action="/mgmt/login" method="post" class="form-horizontal" style="margin: 0 auto; max-width: 360px;">
        {{ csrfField() }}
        <div class="form-group">
          <label for="userid" class="col-sm-3 control-label">아이디</label>
          <div class="col-sm-9">
            <input type="text" id="userid" name="userid" class="form-control" placeholder="당신의 ID를 입력하세요..." required autofocus>
          </div>
        </div>
        <div class="form-group">
          <label for="passwd" class="col-sm-3 control-label">비밀번호</label>
          <div class="col-sm-9">
            <input type="password" id="passwd" name="passwd" class="form-control" placeholder="비밀번호를 입력하세요..." required>
          </div>
        </div>
        <input type="submit" class="btn btn-primary btn-block" value="로그인" />
      </form>
    </div>
     
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    </body>
    </html>
  • xyz/app/Controllers/Http/Mgmt/MainController.js 를 생성합니다.
    'use strict'
     
    class MainController {
      index ({view}) {
        return view.render('mgmt/index')
      }
    }
     
    module.exports = MainController
  • xyz/start/routes.js 다음을 추가합니다.
      Route.get('/', 'Mgmt/MainController.index').as('mgmt_index')
  • xyz/app/Models/Admin.js 에 다음을 추가합니다.
    ...
     
      static async isMember(userid, password) {
        const enc_passwd = crypto.createHash('sha256').update(password).digest('base64')
        const cnt = await this.query().whereRaw('userid = ? and password = ?', [userid, enc_passwd]).count('id as cnt')
        return cnt[0]['cnt'] == 0 ? false : true;
      }
     
    ...
  • xyz/app/Controllers/Http/Mgmt/MainController.js 에 Admin모델을 포함시키고, 다음을 추가한다.
    ...
     
    const Admin = use('App/Model/Admin')
     
    ...
     
      // 로그인
      async login ({request, response, session}) {
        let userid = request.input('userid')
        let passwd = request.input('passwd')
     
        let is_member = await Admin.isMember(userid, passwd)
        if (is_member) {
          await session.put('mgmt-login', true)
          return response.route('mgmt_admin')
        }
     
        return response.route('mgmt_index')
      }
     
    ...
  • xyz/start/routes.js 에 다음을 추가한다.
    ...
     
      Route.post('/login', 'Mgmt/MainController.login').as('mgmt_login')
     
    ...
  • xyz/resources/views/mgmt/admin/index.edge 에 다음을 적당한 곳에 추가한다.
    ...
     
    <a href="/mgmt/logout">[로그아웃]</a>
     
    ...
  • xyz/app/Controllers/Http/Mgmt/MainController.js 에 다음을 추가한다.
    ...
     
      // 로그아웃
      async logout ({session, response}) {
        await session.forget('mgmt-login')
        return response.route('mgmt_index')
      }
     
    ...
  • xyz/start/routes.js 에 다음을 추가한다.
    ...
     
      Route.get('/logout', 'Mgmt/MainController.logout').as('mgmt_logout')
     
    ...
    이제, 로그인도 해보고, 로그아웃도 해보세요. ^^
  • AuthMgmt 미들웨어를 생성한다.
    $ cd xyz
    $ adonis make:middleware AuthMgmt
  • xyz/app/Middlware/AuthMgmt.js 내용은 아래와 같이 작성한다.
    'use strict'
     
    class AuthMgmt {
      async handle ({ session, response }, next) {
        // call next to advance the request
        const mgmt_login = await session.get('mgmt-login')
        if (mgmt_login != true) {
          return response.route('mgmt_index')
        }
        await next()
      }
    }
     
    module.exports = AuthMgmt
  • xyz/start/kernel.js 에 AuthMgmt 를 추가한다.
    ...
     
    const namedMiddleware = {
    ...
     
      auth_mgmt: 'App/Middleware/AuthMgmt'
     
    ...
    }
     
    ...
  • xyz/start/routes.js 파일을 아래처럼 수정한다.
    const Route = use('Route')
     
    Route.group('mgmt', function () {
      Route.get('/', 'Mgmt/MainController.index').as('mgmt_index')
      Route.post('/login', 'Mgmt/MainController.login').as('mgmt_login')
      Route.get('/logout', 'Mgmt/MainController.logout').as('mgmt_logout')
    }).prefix('/mgmt')
     
    Route.group('mgmt_secure', function () {
      Route.get('/admin', 'Mgmt/AdminController.index').as('mgmt_admin')
      Route.get('/admin/insert_form', 'Mgmt/AdminController.insert_form').as('mgmt_admin_insert_form')
      Route.post('/admin/insert', 'Mgmt/AdminController.insert').as('mgmt_admin_insert')
      Route.get('/admin/chg_passwd_form/:id', 'Mgmt/AdminController.chg_passwd_form').as('mgmt_admin_chg_passwd_form')
      Route.post('/admin/chg_passwd', 'Mgmt/AdminController.chg_passwd').as('mgmt_admin_chg_passwd')
      Route.get('/admin/update_form/:id', 'Mgmt/AdminController.update_form').as('mgmt_admin_update_form')
      Route.post('/admin/update', 'Mgmt/AdminController.update').as('mgmt_admin_update')
      Route.get('/admin/delete/:id', 'Mgmt/AdminController.delete').as('mgmt_admin_delete')
    }).prefix('/mgmt').middleware('auth_mgmt')

2017-10-24

(AdonisJs 4.0 Tutorial) 개인정보 양방향암호화(AES256) 하기


  • 이 예제에는 개인정보 데이터가 없긴 하지만, 별명(Nick)을 개인정보라 간주하고 암호화를 진행해보겠습니다.
  • Adonis 에서는 데이터 암호화를 위해서 Encryption 모듈을 제공하고 있습니다.
  • 데이터를 암호화하고 다시 풀려면, Key 가 있어야 합니다. 이 Key 를 .env 파일에 APP_KEY 로 이미 정의되어 있습니다.
  • xyz/app/Models/Admin.js 에 다음을 추가한다.
    const Encryption = use('Encryption')
     
    ...
     
      getNick(nick) {
        return Encryption.decrypt(nick)
      }
     
      setNick(nick) {
        return Encryption.encrypt(nick)
      }
     
    ...
  • DB의 내용을 모두 삭제한 후에, 관리자를 추가해봅니다. 그리고, DB에서 Nick 이 암호화되어 있는지도 봅시다.
    $ cd database
    $ sqlite3 development.sqlite
    SQLite version 3.15.0 2016-10-14 10:20:30
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> select nick from admins;
    eyJpdiI6IkoxVWhqL2w5V1F4eU5iS2xWSk9ZZUE9PSIsInZhbHVlIjoiWmhuSmJLOWZJb3ZXOWNZM0tHaG1lUT09IiwibWFjIjoiNzMzOGUxNDkzMGJjMGVjNDhlM2NiOGMwZjlkZjdiNDY4NDkxODFmOWRkNTlmZjk1NzczMTBkYTgxNjMyM2QyYSJ9
    sqlite> .quit
    제대로 암호화되어서 무슨 내용인지 알아볼 수 없네요. ^^;

2017-10-23

(AdonisJs 4.0 Tutorial) 비밀번호 단방향암호화(SHA256) 하기


  • 정보통신망법, 개인정보보호법 등에 의하면 비밀번호는 단방향암호화(SHA256 등), 개인정보는 양방향암호화(AES256 등)를 하도록 되어 있습니다. 지금까지 작성한 예제는 이런 법에 맞지 않아서 서비스할 수 없겠네요. 여기에서는 비밀번호의 단방향 암호화를 구현해보도록 하겠습니다. Adonis 에서는 비밀번호 암호화에 더 좋다고 하는 bctypt를 제공하고 있지만, 이것이 법에 명시되어 있지는 않아서, SHA256 을 사용하겠습니다.
  • 먼저, 기존 DB에 들어있던 사용자 정보를 모두 삭제합시다.
    $ cd database
    $ sqlite development.sqlite
    SQLite version 3.15.0 2016-10-14 10:20:30
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> select * from admins;
    1|testid1|passwd1|nick1
    2|testid2|passwd2|nick2
    3|testid3|passwd3|nick3
    4|testid4|passwd4|nick4
    6|admin|dpeltms3|ADMIN
    sqlite> delete from admins;
    sqlite> select * from admins;
    sqlite> .quit
  • xyz/app/Models/Admin.js 에 crypto 모듈을 추가하고, setPassword 함수를 추가합니다.
    ...
     
    const crypto = require('crypto')
     
    ...
     
      setPassword(password) {
        return crypto.createHash('sha256').update(password).digest('base64')
      }
     
    ...
  • 이제, 관리자를 추가해보고, DB에서 비밀번호가 어떻게 들어가 있는지 확인해봅니다.
    $ cd database
    $ sqlite3 development.sqlite
    SQLite version 3.7.9 2011-11-01 00:52:41
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> select * from admins;
    8|myid|d49434a7e6f3220b6ec15bff84429934d519d0443d20ff46ff3a36e1fc50fc2d43c939d259042987162202dcbbc8788752ea79c02379064ab64363b069278c32|마이이이디
    sqlite> .quit
    비밀번호가 사람이 알아볼 수 없는 형태로 되어 있지요? 그러면 잘 된 것입니다. *^^*

2017-10-20

(AdonisJs 4.0 Tutorial) 관리자 삭제하기


  • xyz/Controllers/Http/Mgmt/AdminController.js 에 다음을 추가한다.
    ...
     
      // 관리자 삭제
      // /mgmt/admin/delete/:id
      async delete ({params, response}) {
        const admin = await Admin.find(params.id)
        await admin.delete()
        return response.route('mgmt_admin')
      }
     
    ...
  • xyz/start/routes.js 에 다음을 추가하고, 관리자 삭제를 해봅니다.
      Route.get('/admin/delete/:id', 'Mgmt/AdminController.delete').as('mgmt_admin_delete')
  • 여기까지 하면, 기본적인 CRUD 기능을 모두 작성할 수 있게 됩니다. *^^*

2017-10-19

(AdonisJs 4.0 Tutorial) 관리자 수정하기


  • xyz/resources/views/mgmt/admin/update_form.edge 을 작성한다.
    <div class="modal-header">
      <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
      <h4 class="modal-title">관리자 수정</h4>
    </div>
    <div class="modal-body">
      <form name="update_form" action="/mgmt/admin/update" method="post">
        {{ csrfField() }}
        <div class="form-group">
          <label>아이디</label>
          <input type="text" name="userid" class="form-control" readonly required pattern="[a-zA-Z0-9]+" value="{{ admin.userid }}"/>
          <input type="hidden" name="id" class="form-control" value="{{ admin.id }}" />
        </div>
        <div class="form-group">
          <label>별명 <small>(필수)</small></label>
          <input type="text" name="nick" class="form-control" required value="{{ admin.nick }}"/>
        </div>
        <div class="form-group" style="text-align: right">
          <input class="btn btn-primary" type="submit" value="관리자 수정" />
        </div>
      </form>
    </div>
  • xyz/Controllers/Http/Mgmt/AdminController.js 에 다음을 추가한다.
    ...
     
      // 관리자 수정 폼
      // /mgnt/admin/update_form/:id
      async update_form ({params, view}) {
        const admin = await Admin.find(params.id)
        return view.render('mgmt/admin/update_form', { admin: admin.toJSON() })
      }
     
    ...
  • xyz/start/routes.js 에 다음을 추가한다.
      Route.get('/admin/update_form/:id', 'Mgmt/AdminController.update_form').as('mgmt_admin_update_form')
  • 이제, 수정 버튼을 클릭하면 수정할 수 있는 폼이 나타나게 될 것이다. 마지막으로 실제로 수정을 처리하는 루틴을 작성하고 라우터에 등록하자.
  • xyz/app/Models/Admin.js 에 다음을 추가한다.
    ...
     
      static async update (id, nick) {
        let admin = await this.find(id)
        admin.nick = nick
        await admin.save()
      }
     
    ...
  • xyz/Controllers/Http/Mgmt/AdminController.js 에 다음을 추가한다.
    ...
     
      // 관리자 수정
      // /mgmt/admin/update
      async update ({request, response}) {
        let id   = request.input('id')
        let nick = request.input('nick')
     
        await Admin.update(id, nick)
        return response.route('mgmt_admin')
      }
     
    ...
  • xyz/start/routes.js 에 다음을 추가하고, 수정 작업을 진행해보자.
      Route.po