ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Authentication - Passport
    JavaScript/Node.js 2018. 12. 7. 03:02

    Authentication - Passport

    Authentication(인증)이란 회원을 확인하는 절차이다.

    예를 들어 A라는 사용자가 Jamong이라는 닉네임을 사용하는 회원이라고 가정해보자.

    만약 이 A라는 사람이 Jamong인지 주장한다고 했을 때 과연 서버에서는 그 주장을 어떻게 판단할 수 있을까?

    그건 지금까지 우리가 해왔던 데이터 베이스를 통해 사용자 정보를 저장하고, A가 Jamong이라는 것을 주장할 때 그것을 판단하기 위해 데이터 베이스에 저장된 사용자 정보를 확인하고 인증시켜주는 작업을 통해 알 수가 있었다.

    즉 우리가 지금껏 해왔던 회원가입과 그 정보로 로그인하는 것이 인증 절차 과정인 것이다.


    현대에는 다양한 인증 절차 과정 기술이 존재한다.

    회원이 정보를 우리의 데이터 베이스에 저장하여 관리하는 방식의 Local Authentication(로컬 인증) 방법과 페이스북, 아마존, 구글, 카카오등 각 기업들에서 제공하는 API를 통해 회원 정보를 전달받는 Federation Authentication(타사 인증) 방법이 있다.

    이러한 인증 방법들을 쉽게 구현할 수 있게 도와주는 Passport라는 패키지가 존재한다.


    오늘은 이 Passport 패키지를 알아보고 기존에 작업했던 로컬 인증 방법을 Passport를 통해 구현해보자.




     Passport

    npm 사이트에서 passport를 검색해보면 아래와 같은 패키지를 찾을 수 있다.


    "Passport는 Node.js를 위한 Express호환 인증 미들웨어이다.

    Passport의 유일한 목적은 어떠한 목적을 위해 확장 가능한 플러그인을 통해 수행되는 요청을 인증하는 것이다.

    Passport는 라우트를 탑재하지 않고 유연성을 극대화해 개발자가 애플리케이션 레벨의 결정을 할 수 있는 특정 데이터 베이스 스키마를 취하지 않는다.

    Passport에게 인증 요청을 제공하고 Passport는 인증이 성공하고 실패할 때 발생하는 것을 제어하기위해 훅을 제공한다."

    상단의 Simple, unobtrusive authentication for Node.js 그림을 클릭하면 passport의 사이트로 이동된다.

    해당 사이트엔 API와 사용방법이 자세하게 설명되어 있다.

    http://www.passportjs.org/




     Passport 설치하기

    npm install passport --save를 통해 패키지를 설치할 수 있다.




     Passport 사용방법

    "Passport는 인증 요청을 위한 전략의 개념을 사용한다. 

    Strategies는 사용자 이름과 비밀번호 자격을 검증하고, 위임된 인증인 OAuth(트위터나 페이스북같은)를 사용하고, OpenID를 사용한 연합된 인증등 다양하다.

    요청을 인증하기 전에 애플리케이션에서 사용하는 strategy를 구성해야한다."

    passport.use()를 통해 LocalStrategy객체를 생성하고 그 안에 다양한 설정들을 구성해야하는 것 같다.


    "Passport는 로그인 세션이 유지된다. 지속적인 세션이 작동하려면 인증된 사용자를 세션에 직렬화하고, 그 다음 요청이 만들어지면 역직렬화를 해야한다.

    Passport는 사용자 레코드를 저장하는 것을 제한하는 걸 강요하지 않는다. 대신, Passport에 필요한 직렬화 및 역직렬화 논리를 구현하기 위해 기능을 제공한다. 일반적인 애플리케이션에서 사용자 ID를 직렬화하고 역직렬화할 때 ID로 사용자를 찾는 것은 간단하다."

    세션을 유지할 수 있게 사용자 데이터를 serializeUser()함수와 deserializeUser()함수를 사용해서 직렬화 및 역직렬화 한다.


    "Express 또는 Connect 기반 애플리케이션에서 Passport를 사용하려면, passport.initialize()미들웨어를 구성을 해야한다. 애플리케이션이 영구 로그인 세션을 사용하는 경우, passport.session() 미들웨어를 사용해야한다."

    Express 웹 프레임워크에서 Passport를 사용하려면 passport.initialize() 미들웨어를 사용선언해야하고 로그인 세션을 유지하는 기능을 포함할 거라면 passport.session()미들웨어도 사용선언해야한다.






     Passport configuration

    사용 방법을 통해 알게된 설정들을 직접 애플리케이션에 작성해보자.

    app_session_mysql.js를 열고 아래의 내용을 작성한다.

    설치한 passport 패키지를 사용하기 위해 require()로 선언한다.

    일단 로컬 데이터 베이스에서 관리하는 사용자 정보를 사용해 로그인 및 회원가입을 하고 있으니 LocalStrategy를 사용할 것이다.

    "npm install passport-local --save"구문으로 passport-local 패키지를 설치하고 require()로 선언한다.


    그리고 passport를 사용하기 위해 초기화하는 initialize() 메소드를 사용선언하고 로그인에 대한 세션을 유지하기 위해 session()메소드를 사용선언한다.



    LocalStrategy 객체를 생성하고 각종 설정을 해보자.

    자바스크립트에서 new키워드를 앞에 붙이면 객체를 생성할 수 있다.

    데이터 베이스에 조회 쿼리를 전달하여 사용자 정보를 조회하고 조회된 정보가 있으면 조회된 정보의 사용자 아이디와 요청으로 전달된 사용자 아이디를 비교한다.

    비교해서 아이디와 패스워드가 인증된 경우 done()함수 인자에 null과 userinfo정보를 전달하고 return구문을 통해 현재 콜백함수를 종료시킨다.

    만약 인증되지 않은 패스워드이거나 아이디인 경우 done()함수 인자에 null과 false를 전달하며 현재 콜백함수를 종료시킨다.


    done()함수의 정보는 passport홈페이지의 Documentation 메뉴에서 Verify Callback메뉴에서 설명을 볼 수 있다.

    http://www.passportjs.org/docs/configure/



    이제 serializeUser()메소드와 deserializeUser()메소드를 작성하자.

    - serializeUser() -

    LocalStrategy객체를 생성하고 콜백함수를 정의했을 때 사용했던 done()함수가 call될 때 최초 1회 serializeUser()메소드가 실행된다.


    LocalStrategy객체를 생성하며 정의했던 콜백함수에서 done()함수를 통해 전달했던 user정보(userinfo)와 done함수를 serializeUser()의 콜백함수 인자로 전달받게 된다. 

    이 때 serializeUser()메소드에서 user객체 외에 인자로 받은 done함수를 사용해서 식별할 수 있는 정보 userinfo.username(사용자 id)를 세션에 저장한다.


    - deserializeUser() -

    사용자가 다른 페이지를 접속할 때 마다 deserializeUser()를 호출하게 되고 serializeUser()를 통해 세션에 저장한 userinfo.username 정보를 id라는 인자로 받게 된다.

    그럼 이 아이디 값을 이용해서 데이터 베이스를 통해 userinfo테이블을 조회하고 그 사용자 정보를 done()함수를 통해 req객체의 user라는 속성을 채우게 된다.


    설명이 상당히 복잡한데 Java 웹 애플리케이션으로 설명하자면 일종의 로그인 Interceptor가 수행하는 역할을 한다고 생각하면 될 것 같다.




     Passport로 로그인 처리하기

    Passport를 사용할 수 있도록 세팅을 완료했으니 이제 Passport를 통해 로그인을 처리해보자.

    가장 먼저 로그인 폼을 수정해야한다.

    바로 input태그에서 name을 변경했는데 id를 입력하는 태그의 name과 password를 입력하는 태그의 name이 변경되었다.

    - user_id → username

    - user_pwd → password

    변경한 이유는 Passport에서 폼 데이터에 접근할 때 username과 password로 사용자 정보를 찾도록 설정되어 있기 때문이다.

    이 설정은 변경할 수도 있다고 하는데 나중에 필요할 때 변경하도록 하자..


    로그인 폼을 수정했다면 로그인 처리 비즈니스 로직을 수정하자.

    기존 로그인 처리는 익명함수로 처리했었는데 그것을 모두 지우고 "passport.authenticate()"미들웨어가 대체한다.

    그 안에 두번째 인자로 받는 객체 옵션이 존재하는데 그 옵션에 대한 설명은 passport 홈페이지에서 알 수 있다.


    기본적으로 인증에 성공하면 리다이렉트를 발생시킨다. 인증에 실패하면 로그인 페이지로 리다이렉션 시키는 옵션들을 제공하고 있다.


    failureFlash는 처리가 실패되었을 때 처리 메시지를 사용자에게 표현하는 기능이다.

    true를 설정하고 사용해도 되고 false로 비활성화 해도 된다.


    - 로그인 처리 작업 순서도 - 

    이런 순으로 로그인 작업이 처리된다.




     /welcome 라우트 수정

    로그인이 정상적으로 처리되면 /welcome으로 리다이렉션을 연결하기 때문에 /welcome라우트를 수정해야한다.

    /welcome은 로그인 된 사용자인 경우 닉네임을 화면에 표현해주고 로그인되지 않은 사용자인 경우 로그인 메뉴와 회원가입 메뉴를 표현해준다.


    이 때 이전에는 우리가 req객체의 session속성에 displayName(nickname)정보를 직접 저장하고 표현해줬었다.

    하지만 현재는 로그인을 처리할 때 req객체에 사용자 정보를 담는 작업을 deserializeUser()에서 하고 있다.

    이 과정은 deserializeUser()가 일을 처리하는 과정 순서도인데 serializeUser()를 통해 사용자 정보를 세션에 저장하고 있는 상황에서 리다이렉션이나 요청이 발생하면 deserializeUser()가 인터셉트하게 된다.

    이 때 deserializeUser()는 세션에 저장된 사용자 식별자 정보를 이용해서 데이터 베이스에 사용자 정보를 조회한다.

    그리고 조회된 정보를 req객체에 user라는 속성으로 생성하게 된다.

    이 작업이 완료되면 /welcome페이지로 요청을 연결하고 req객체에 user 속성을 함께 전달해준다.


    따라서 세션에 직접 데이터를 저장하고 세션의 데이터 유무를 판단하는 방법은 사용하지 않고 passport로 인해 생성된 req객체에 user속성에 저장된 데이터의 유무를 판단한다.




     로그인 실행하기

    /welcome URL로 접속하고 로그인을 수행해보자.

    로그인이 정상적으로 처리된다면 아무런 문제가 없는 것이다.


    만약 로그인을 하는데 로그인이 처리되지 않고 login페이지로 리다이렉션이 된다면 로그인에 실패됬다는 의미다.

    입력한 정보들을 다시 확인해보거나 코드 작성을 잘못한 것은 없는지 다시 한번 판단해보자.




     로그아웃 수정하기

    세션에 식별자를 직접 생성하지 않으므로 로그아웃 처리도 수정해야한다.

    passport 홈페이지의 docmuentation을 보면 LogOut기능에 대한 API문서를 제공하고 있다.

    Passport에서는 logout()함수를 req객체에 포함하고 로그인 세션을 종료할 필요가 있는 라우트 핸들러에서 호출하여 사용할 수 있다.

    logout()을 호출하면 req.user 속성이 제거되고 로그인 세션이 지워진다.


    req.logout()으로 로그인 세션을 제거하고 세션 제거 처리가 완료될때까지 기다린 후 리다이렉션을 수행하기 위해 req.session.save()메소드를 사용한다.




     로그아웃 실행하기

    로그아웃 기능을 수정했으니 로그인 한 상태에서 로그아웃을 실행해보자.

    로그아웃 메뉴를 클릭하면 세션 정보가 지워지며 welcome으로 리다이렉션이 된다.




     Passport로 회원가입 처리하기

    이번에는 Passport를 이용해 회원가입을 처리해보자.

    기존에 작성한 회원가입 폼을 수정하자.

    로그인 폼과 동일하게 id 태그의 name을 username으로 수정하고 password 태그의 name을 password로 수정한다.


    회원가입 처리 비즈니스 로직을 수정하자.

    id input 태그의 name을 변경했으므로 req.body.username으로 값을 가져올 수 있게 수정해준다.


    그리고 사용자 정보를 데이터 베이스에 등록한 후 기존 /login페이지로 리다이렉션 시켰던 작업을 바로 로그인 처리가 되도록 수정했다.

    사용자를 등록하는 INSERT문의 결과값 중 insertId 값을 이용해서 새로 등록한 사용자의 정보를 조회한다.

    그리고 조회된 정보를 req.login()함수를 사용하여 로그인 처리를 시도하고 세션에 사용자 정보가 저장되면 /welcome으로 리다이렉션한다.


    passport 홈페이지의 documentation에서 login()에 대한 API 정보를 볼 수 있다.

    Passport에서는 login()함수를 req객체에 포함하고 로그인 세션을 처리하는 곳에서 호출하여 사용할 수 있다.

    로그인 운영에 성공한 경우 user객체를 req.user속성에 할당한다.

    passport.authenticate() 미들웨어는 req.login()함수를 자동으로 호출한다. 이 함수는 주로 사용자 등록(회원가입)할 때 사용되며, 새로 등록된 사용자에 자동으로 로그인하는데 호출할 수 있다.




     회원가입 실행하기

    회원가입 프로세스를 수정했으니 직접 회원가입을 해보자.

    회원가입 창에서 회원정보를 작성하고 제출을 누르면 자동으로 로그인 처리가 되고 /welcome페이지로 리다이렉션이 된다.

    userinfo 테이블에도 신규 사용자 정보가 정상적으로 등록이 되어야한다.


    만약 정상적으로 처리되지 않는 경우 본인이 작성한 코드를 다시한번 둘러보자.

    'JavaScript > Node.js' 카테고리의 다른 글

    모듈화  (0) 2018.12.10
    Federation Authentication  (0) 2018.12.09
    Process Management - PM2  (0) 2018.12.06
    암호화 - MD5, SHA256, PBKFD2  (0) 2018.12.06
    Express - MySQL Session  (0) 2018.12.04
Designed by Tistory.