목적
JWT 인증을 nginx에서 처리하기 위해서 lua 스크립트를 사용했습니다.
현재 프로젝트에서 MSA로 설계했습니다.
이 경우 서버 마다의 JWT 인증을 어떻게 처리할지 고민이였습니다.
nginx는 JWT 파서를 오픈소스로 제공하지 않기 때문에 다른 방법을 사용해야 했습니다.
그래서 kong
과 lua
같은 스크립트 방식, 또한 SPRING GATEWAY API
를 찾아보았습니다.
저희 프로젝트 규모에 맞고, 목적(JWT 인증처리만)에 맞고,
이미 nginx를 통해 리버스 프록시를 사용하고 있기 때문에, LUA
를 사용하고자 했습니다.
(특히 nginx plus에 대한 정보가 많이 부족했습니다.)
아래는 설정 방법입니다.
Lua 스크립트
local function verify_jwt()
local args = ngx.req.get_headers()
local token = args["Authorization"]
if not token then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Missing Authorization header")
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
token = token:match("Bearer%s+(.+)")
if not token then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Invalid Authorization header")
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
local jwt_obj = jwt:verify("OurKey", token)
if not jwt_obj.verified then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Invalid token")
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
ngx.req.set_header("X-User-ID", jwt_obj.payload.sub)
end
return verify_jwt
프론트엔드 코드
javascript
import axios from 'axios';
const response = await axios.get('http://our-backend-domain/api/protected', {
headers: {
Authorization: `Bearer ${token}`, // JWT를 Authorization 헤더에 포함
},
});
// 요청 인터셉터 설정
const apiClient = axios.create({
baseURL: 'http://your-backend-domain',
});
apiClient.interceptors.request.use(
config => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 응답 인터셉터 설정
apiClient.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const refreshToken = localStorage.getItem('refreshToken');
if (refreshToken) {
try {
const response = await axios.post('http://your-backend-domain/api/refresh-token', {
token: refreshToken,
});
const newAccessToken = response.data.accessToken;
localStorage.setItem('accessToken', newAccessToken);
apiClient.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
return apiClient(originalRequest);
} catch (e) {
console.error('Refresh token expired, please log in again', e);
// 로그아웃 처리 또는 사용자에게 재로그인 요청
}
}
}
return Promise.reject(error);
}
);
export default apiClient;
위 경우 401이 나온다면 갱신하는 프로세스를 추가했습니다.
nginx.conf
nginx
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://172.28.0.2:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api {
#access_by_lua_file /etc/nginx/lua/verify_jwt.lua;
proxy_pass http://172.28.0.5:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
listen 80;
listen [::]:80;
return 404; # managed by Certbot
}
}
현재는 배포가 user밖에 안 되어 있기 때문에 주석처리로 확인해보지 않았습니다. 이후 해당 부분을 확인해도 제대로 동작하는지 확인해보고자 합니다.
확인 완료했고 모두 정상 동작합니다 !
'backend > CICD' 카테고리의 다른 글
Mixed Content 에러 해결 (0) | 2024.08.01 |
---|---|
CORS 문제 (1) | 2024.07.31 |
배포자동화 CI/CD AWS EC2, Git Action, Docker (0) | 2023.05.17 |