일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- javascript
- 정리
- login
- 뷰
- Firebase
- AWS
- 회고록
- react
- PWA
- database
- 신상마켓
- extension
- 토이프로젝트
- jwt
- express
- nginx
- vue login
- 로그인
- vsCode
- Docker
- react-query
- 기획
- plugin
- Git
- vue
- 프론트
- 셋팅
- vuex
- 배포
- 플러그인
- Today
- Total
강디너의 개발 일지
javascript - JWT 로그인 및 세션 유지 본문
이전 포스팅과 이어집니다.
광고 보고 가시겠습니다.
목표
1. Access Token을 이용한 API 통신
2. Refresh Token을 이용한 Access Token 갱신
3. 사용자 모르게 토큰 갱신 후 API 호출
아래와 같은 Flow로 사용자에게 세션이 끊켰다는것을 모르게 데이터를 보여줄 것입니다.
준비
front - Vue 만든거 열기
back - API 서버 열기
docker - 만들어둔 컨네이너 시작해서 DB 열기
Git - github.com/DinnerKang/study_vue/tree/master/todo-list
Docker - kdinner.tistory.com/99?category=312484
Login API 입니다.
사용자는 로그인을 해서 토큰을 얻어왔습니다.
토큰을 쿠키에 저장해줍니다. 서버와 협의 후 쿠키에 토큰 유효기간도 넣어줍니다.
현재는 1분 후 토큰이 사라지게 해놨습니다. (테스트를 편하게 하기 위해서)
// Front - login.js
const emailLogin = async (email, password) => {
const data = {
email,
password,
};
try {
const { result } = (await axios.post('/emailLogin', data)).data;
VueCookies.set('access-token', result.access_token, '60s');
VueCookies.set('refresh-token', result.refresh_token, '3d');
console.log(result);
return result;
} catch (e) {
return e;
}
};
--------------------------------------------------
// Back
app.post('/emailLogin', (req, res) => {
const { email, password } = req.body;
if (!email || !password) return res.status(500).json({ result: '아이디나 비밀번호를 입력해주세요.' });
pool.getConnection((err, conn) => {
if (!err) {
conn.query('SELECT * FROM user WHERE email = ?', [email], (err, rows) => {
if (err) return res.status(500).json({ result: err });
if (rows.length === 0) return res.status(500).json({ result: '아이디가 없습니다.' });
if (rows[0].password === password) {
const token = jwt.sign({
email: req.body.email,
info: '토큰에 넣고싶은거',
}, privateKey, { expiresIn: '60s' });
const refreshToken = jwt.sign({
email: req.body.email,
info: '리프레시토큰입니다',
}, refreshKey, { expiresIn: '3d' });
conn.query('UPDATE user SET access_token = ?, refresh_token = ? WHERE email = ?', [token, refreshToken, email], (err, rows) => {
if (err) return res.status(500).json({ result: err });
});
return res.json({ msg: '로그인 성공', result: { access_token: token, refresh_token: refreshToken} });
} else {
return res.status(500).json({ result: '비밀번호가 틀렸습니다.' });
}
});
}
conn.release();
});
});
Test API 입니다.
간단한 토큰 검증만 하고있습니다. 만약 토큰이 없을 경우 에러 메시지를 뱉습니다.
Back 코드를 보시면 토큰 검증 오류일 경우에는 401로 주게 해놨습니다. 이러한 이유는 곧 보여드립니다.
// Front - login.js
const test = async () => {
try {
const data = await axios.get('/testAPI');
console.log('API 성공');
return data;
} catch (e) {
console.log('API 실패');
return e;
}
};
-------------------------------------------------------------------
// Back
app.get('/testAPI', (req, res) => {
const token = req.headers['access-token'];
// 토큰 검증
jwt.verify(token, privateKey, (err, decoded) => {
if (err) return res.status(401).json({ result: err });
return res.json({ msg: '성공', result: { msg: 'success' } });
});
});
Refresh API 입니다.
Refresh API 는 토큰이 정상이면 쿠키에 다시 넣어주는 코드입니다.
// Front
login.js
const refreshToken = async () => {
try {
const { result } = (await axios.get('/refreshToken')).data;
VueCookies.set('access-token', result.access_token);
console.log('Refresh API 성공', result);
return result;
} catch (e) {
console.log(e);
}
}
----------------------------------------------------
// Back
app.get('/refreshToken', (req, res) => {
const token = req.headers['refresh-token'];
console.log('refresh', token);
// 토큰 검증
jwt.verify(token, refreshKey, (err, decoded) => {
if (err) return res.status(500).json({ result: err });
const token = jwt.sign({
email: req.body.email,
}, privateKey, { expiresIn: '60s' });
return res.json({ msg: '리프레쉬 성공', result: { access_token: token } });
});
});
axios.js 입니다.
사용자에게 토큰이 만료되었다고 보여주면 안되니 Refresh Token을 이용하여 토큰을 얻은 후 API를 통신합니다.
토큰이 없을 경우 Back에서 401 status를 보내주는데 axios interceptors에서 401을 잡아주어 refresh API를 타도록 합니다.
그 후 오류 났던 API를 다시 쏴서 통신이 끊키지 않은 것 처럼 사용자에게 성공 메시지를 보여줍니다.
추가적으로 axios interceptors에서 access-token 쿠키 값이 없을 경우 login 페이지로 보내버리는 기능을 할 수도 있습니다.
axios.js
import axios from 'axios';
import VueCookies from 'vue-cookies';
import { refreshToken } from './login';
axios.defaults.baseURL = 'http://localhost:5000';
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.headers['access-token'] = VueCookies.get('access-token');
config.headers['refresh-token'] = VueCookies.get('refresh-token');
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, async function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
console.log('오류:', error.config);
const errorAPI = error.config;
if(error.response.status === 401 && errorAPI.retry === undefined){
errorAPI.retry = true;
console.log('토큰이 이상한 오류일 경우');
await refreshToken();
return await axios(errorAPI);
}
return Promise.reject(error.response);
});
export default axios;
감사합니다.
'Javascript > 삽질' 카테고리의 다른 글
이미지 업로드 - 프론트 부터 AWS S3 까지의 과정 (0) | 2021.04.25 |
---|---|
Node.js - AWS Lambda 용량 줄이기(Layer를 사용하자) (0) | 2021.02.28 |
화면에 특정 DOM 이 보이는가? - InterSectionObserver API (4) | 2020.09.03 |
Firebase realtime database pagination 삽질기 (0) | 2020.05.25 |
Firebase Database CRUD (0) | 2020.02.17 |