일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- MFA
- OpenStack
- REACT
- openapi3
- SpringRESTDocs
- Filter
- gradle
- SpringBoot
- vue
- MSA
- Spring REST Docs
- tasklet
- Pender
- Crawling
- Reduxpender
- Spring Security
- axios
- JavaScript
- SWAGGER
- 리액트
- T-OTP
- stopPropogation
- Flyway
- cloud native
- preventdefault
- vuejs
- Spring Batch
- UsernamePasswordAuthenticationFilter
- AuthenticatoinProvide
- cheerio
- Today
- Total
Miracle Morning, LHWN
1. Node.js express 프레임워크의 session 본문
# 최근 웹 어플리케이션은 쿠키를 통해 직접 인증을 구현하지 않고, 쿠키는 사용자를 식별하는 용도로만 사용한다.
대신 실제 데이터는 session을 통해 서버 쪽에 안전하게 파일이나 데이터베이스의 형태로 저장한다.
# 여기서는 인증 라이브러리인 express를 사용해본다. (※ plugin과 middleware는 비슷한 개념)
(※ express 는 프론트인 React 를 이용해서 백엔드 서버를 만들 수 있도록 해주는 프레임워크이다.)
npm install -s express session
// 여기서 -s는 "이 프로젝트는 express session 모듈에 의존하고 있다"는 의미
# 예제 소스 (express session github에서 공식적으로 제공하고 있는 샘플 소스)
// nodejs/express-session.js
var express = require('express')
var parseurl = require('parseurl')
var session = require('express-session')
var app = express()
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
app.use(function (req, res, next) {
if (!req.session.views) {
req.session.views = {}
}
// get the url pathname
var pathname = parseurl(req).pathname
// count the views
req.session.views[pathname] = (req.session.views[pathname] || 0) + 1
next()
})
app.get('/foo', function (req, res, next) {
res.send('you viewed this page ' + req.session.views['/foo'] + ' times')
})
app.get('/bar', function (req, res, next) {
res.send('you viewed this page ' + req.session.views['/bar'] + ' times')
})
app.listen(3000, function () {
console.log('Port 3000');
});
# nodemon도 추가로 설치해보았다.
※ nodemon : 서버 코드를 변경할 때마다 서버를 재시작하는게 귀찮을 때 nodemon을 사용하면 이를 자동으로 재시작해준다.
※ nodemon : 서버 코드를 변경할 때마다 서버를 재시작하는게 귀찮을 때 nodemon을 사용하면 이를 자동으로 재시작해준다.
# 위 소스를 실행하면 사용자를 식별할 수 있는 connect.sid 쿠키 값이 생성된다.
서버에 접속할 때마다 (새로고침) 웹 브라우저가 서버로 connect.sid 쿠키 값을 전송해주고,
서버에서는 쿠키 값을 가지고 그 쿠키에 해당하는 사용자의 데이터를 조작한다 = 1씩 증가해준다.
session middleware의 동작
서버 <> 클라이언트간 HTTP 통신을 할 때, 서버가 쿠키를 발행하고 클라이언트가 쿠키를 저장한다.
그리고 나중에 재접속을 할 때 서버는 클라이언트의 쿠키 값을 확인한다. (비교 주체 : 쿠키)
만일 세션을 이용한다면 쿠키에 세션의 sid 값만을 저장해둔다. 따라서 서버에서는 sid 값을 가지고 서버 내부에 저장되어 있는
데이터베이스 혹은 파일과 비교하여 내용을 확인하는 메커니즘인 것이다. (비교 주체 : 세션의 sid) → 보안 강화
session을 이용한 기본 동작 구현
→ 결과 : 리로드 될 때마다 세션의 num 값이 증가된다.
var express = require('express')
var parseurl = require('parseurl')
var session = require('express-session')
var app = express()
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
app.get('/signIn', function (req, res, next) {
console.log(req.session); // 위 소스가 req 안에 session 객체를 추가한다.ㅏ
if (req.session.num === undefined) {
req.session.num = 10; // 처음에는 num 안에 값이 없기 때문에 10이 지정됨
} else {
req.session.num += 1; // 이후 요청부터는 1씩 더해짐
}
res.send(`Views : ${req.session.num}`);
})
app.listen(3000, function () {
console.log('Port 3000');
});
# app.use() : 사용자 요청이 있을 때마다 해당 코드가 실행되도록 약속.
# secret : id 값. 식별자
→ 나만 가지고 있어야 하는 비밀옵션!
→ 나중에 프로젝트에서는 별도의 파일에 저장해놓고 변수처리해놔야 한다.
<!-- resave, saveUninitialized는 특별한 이유가 있지 않은 이상 아래처럼 세팅 -->
# resave: false
→ 세션 데이터에 변화가 있을 때에만! 저장한다.
(true면 계속 저장한다.)
# saveUninitialized: true
→ 세션이 필요하기 전까지는 세션을 구동시키지 않는다.
(false면 세션이 필요하든 필요없든 세션을 구동시킨다.)
app.use(session({ // app이 session을 사용. session 함수 실행 → 미들웨어가 내부적으로 개입
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
근데 위 예제에서 num은 휘발성 데이터이기 때문에 서버를 재구동하면 데이터가 초기화된다.
이를 해결하기 위해 File 형태의 store에 저장해보자!
session 객체의 옵션 알아보기
var express = require('express')
var parseurl = require('parseurl')
var session = require('express-session') // 미들웨어를 모듈로서 설치해준다.
var FileStore = require('session-file-store')(session);
var app = express()
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
store: new FileStore()
}))
app.get('/signIn', function (req, res, next) {
console.log(req.session);
if (req.session.num === undefined) {
req.session.num = 10;
} else {
req.session.num += 1;
}
res.send(`Views : ${req.session.num}`);
})
app.listen(3000, function () {
console.log('Port 3000');
});
호환되는 store을 선택해서 구현을 할 수 있는데 여기에서는 File 형태에 저장할 것이다.
(외 호환되는 방식은 링크 참고 : http://expressjs.com/en/resources/middleware/session.html)
var FileStore = require('session-file-store')(session);
위 소스를 실행하면 req.session 내용이 파일로 생성되어 저장되며, 생성된 파일을 보면 요청할 때마다 num 값이 바뀌는 것을 볼 수 있다.
(실제 프로젝트에서는 session 내용을 데이터베이스나, 해싱 데이터베이스에 저장해두어야 한다.)
→ 사용자의 계정 (닉네임 등)은 세션에 담아두면 데이터베이스나 파일에 다시 접속할 필요가 없어서 효율적이다.
사용자가 sessionId를 가지고 있는 상태에서 서버로 접속하면, 쿠키에 sessionId를 담아 서버로 전달한다.
→ 미들웨어가 sessionId를 가지고 세션 store에서 해당 Id 값에 대응하는 파일을 읽는다.
→ 파일 데이터를 기반으로해서 req 객체의 session 프로퍼티에 객체를 추가해준다.
→ 사용자가 num 값을 바꾸면 (+1) req.send를 통해 파일의 값을 또 업데이트해준다.
로그인 구현
전체 소스는 강사님의 git 에 있으니 참고!
https://github.com/web-n/express-session-auth/tree/f4630ffd99e5edd6d4baaef1b5a36a9f83b982fc
# 로그인했을 때! HTTP 응답(response) header로 connect_sid라는 세션의 식별자를 쿠키에 저장한다.
→ 해당 connect_sid 에 해당하는 파일은 서버쪽에 저장한다.
# 로그아웃할 때는 request.session.destroy 함수를 이용한다.
→ 로그인할 때 생성되었던 파일이 삭제되면서 다시 로그아웃에 대한 새로운 파일이 생성된다. (로그아웃에 대한 새로운 세션을 발급한 것이다.)
세션 저장 작업
# 아래와 같이 로그인 유효성 검사를 하는 로직이 있다고 하자.
// 문제가 좀 있는 소스
if(email === authData.email && password === authData.password {
request.session.is_logined = true;
request.session.nickname = authData.nickname;
response.redirect('/');
} else {
response.send('who?');
}
위 과정이 모두 끝난 다음에 세션 미들웨어는 우리가 기록한 데이터(is_logined, nickname)를 세션 store에 기록하는 작업을 한다.
(메모리에 저장된 세션 데이터를 저장소에 반영하는 작업이다.)
근데 저장소에 담겨있는 데이터의 양이 많아져서 저장하는 작업 속도가 현저히 느려진다면,
값이 바뀌기도 전에 response.redirect('/'); 가 실행될 수도 있다. (저장소에 값을 저장하지도 않았는데 redirect가 발생하는 것이기 때문에
사용자 입장에서는 '나는 로그인을 했는데 로그인이 안된 상태의 '/' 페이지가 보여지네', '아까 로그인 안되더니 한참뒤에 보니까 또 로그인이 되어있네')
# 이런 현상을 방지하기 위해 사용하는 것이 request.session.save() 함수이다.
이 save() 함수를 이용하면 세션 객체(request.session)에 있는 데이터 (is_logined, nickname)를 세션 store에 반영하는 작업을 바로 시작한다.
그리고 이 작업이 모두 끝난 뒤에야 인자로 전달된 콜백함수를 실행하게 된다. 이 콜백함수에는 당연히 redirect 함수를 담아야 할 것이다.
즉, store에 반영작업이 모두 끝난 뒤에 redirect가 일어날 수 있도록 보장하는 작업이다.
// 문제를 해결한 소스
if(email === authData.email && password === authData.password {
request.session.is_logined = true;
request.session.nickname = authData.nickname;
reqeust.session.save(function() {
response.redirect('/');
})
} else {
response.send('who?');
}
로그인 인증에 대한 보안
# secure 옵션
HTTP로 웹 서버 <> 브라우저간 통신을 하게되면 누군가가 connect_sid를 가로채서 대신 로그인 할 수도 있다.
그래서 HTTPS로 통신을 해주어야 하는데 secure 옵션에 true 값을 주면 HTTPS에서만 세션 정보를 주고받을 수 있도록 처리할 수 있다.
app: use(session({
secure: true,
secret: 'xxx',
resave: false,
saveUninitialized: true,
store: new FileStore()
}))
# HttpOnly 옵션
사용자가 전송하는 데이터에 스크립트가 포함되어 있고, 그 스크립트가 활성화될 수 있는 환경이라면 사용자는 스크립트를 이용해서
세션 쿠키 아이디를 탈취해서 공격자에게 전송할 수 있다. 이럴 경우에는 사용자가 전송하는 데이터에서 스크립트를 사용할 수 없도록 설정해주어야 할 것이다.
세션의 옵션으로 HttpOnly: true 으로 지정하여 자바스크립트를 통해서 세션 쿠키를 사용할 수 없도록 강제할 수 있다.
app: use(session({
HttpOnly: true,
secret: 'xxx',
resave: false,
saveUninitialized: true,
store: new FileStore()
}))
타사인증 (federation authentication) 도구들
일반적인 서비스는 사용자의 정보를 데이터베이스에 저장하고 이를 활용하는 방향으로 운영되기에 '회원가입'과 '로그인' 과정이 필수적으로 요구된다. 그러나 최근에는 facebook, twitter, github, google, kakao 등 타사의 유저 정보를 이용한 회원가입 및 로그인 서비스 즉, 타사인증 (Federation Authentication) 을 도입해 인증체계는 대기업에서 수행하도록 하여 안전하고 간편하게 구현한다.
타사인증을 해주는 도구는 oauth가 있는데 이를 또 대신해주는 도구로 Node.js의 대표적인 인증 라이브러리(통합 인증 관리 패키지)인 passport-js가 있다.
'IT 기술 > [React] Project' 카테고리의 다른 글
2_0. 조회(GET), 삭제(DELETE), 수정(PUT, PATCH) (0) | 2021.06.01 |
---|---|
2. Mongoose 를 통한 MongoDB 연동 & 생성 (POST) (0) | 2021.06.01 |
1_0. 이것저것 궁금해서 찾아본 것들 정리 (0) | 2021.06.01 |
0_0. 환경 세팅하면서 이것저것 궁금해서 찾아본 것들 정리 (0) | 2021.05.31 |
0. 프로젝트 시작하기 (0) | 2021.05.31 |