인증 및 권한 부여는 응용 프로그램의 큰 부분입니다. 보호 또는 검사가 없는 API 경로가 있을 때마다 애플리케이션은 쉽게 해커의 표적이 될 수 있습니다. 그렇기 때문에 보안 토큰, 즉 JWT (JSON Web Token)가 필요합니다.
https://dev.to/calvinqc/a-step-by-step-guide-to-setting-up-a-node-js-api-with-passport-jwt-5fa5
JWT의 기본 사항
JWT에 너무 깊이 들어 가지 않겠지만 여기에 모든 기본 사항이 있습니다.
JWT (JSON Web Token)는 당사자 간에 정보를 JSON 개체로 안전하게 전송하기 위한 컴팩트 하고 독립적 인 방법을 정의하는 개방형 표준 (RFC 7519)입니다. 이 정보는 디지털 서명이 되어 있으므로 확인하고 신뢰할 수 있습니다. JWT는 비밀 (HMAC 알고리즘 사용) 또는 RSA 또는 ECDSA를 사용하는 공개 / 개인 키 쌍을 사용하여 서명 할 수 있습니다.
JSON 웹 토큰은 사용자 정보를 인코딩하고 디코딩 합니다. 인증 및 정보 교환에 사용됩니다.
다음과 같이 점 (.)으로 구분 된 세 부분 (헤더, 페이로드 및 서명)으로 구성됩니다. xxxxx.yyyyy.zzzzz
여기에서 JSON 웹 토큰에 대해 자세히 알아보세요.
시작하기 전에
이 비디오를 확인하지 않으면 컴퓨터에 이미 npm이 있고 Postman으로 테스트한다고 가정합니다.
절차에 문제가 있는 경우 코드가 있고 질문이 필요한 경우 Trivin의 Slack에 가입하세요.
서버 설정
자체 서버를 사용하려면 이 단계를 건너 뛰십시오.
프로젝트가 없는 경우 Trivin을 사용하여 프로젝트 템플릿을 설정합니다. 이 기사에서는 이를 사용하여 단순 노드 서버를 만듭니다.
$ npm i trivin -g
$ trivin server simple-node-server -g -i
이렇게 하면 간단하지만 잘 구성된 노드 서버가 생성되고 Git을 초기화하고 모든 프로젝트 종속성이 설치됩니다.
설치
$ npm i passport passport-jwt winston cors express-validator jsonwebtoken
지원 파일 설정
$ mkdir store/
$ touch store/passport.js store/config.js store/utils.js controller/constant.js
Constant.js
export const EMAIL_IS_EMPTY = 'EMAIL_IS_EMPTY';
export const PASSWORD_IS_EMPTY = 'PASSWORD_IS_EMPTY';
export const PASSWORD_LENGTH_MUST_BE_MORE_THAN_8 =
'PASSWORD_LENGTH_MUST_BE_MORE_THAN_8';
export const WRONG_PASSWORD = 'WRONG_PASSWORD';
export const SOME_THING_WENT_WRONG = 'SOME_THING_WENT_WRONG';
export const USER_EXISTS_ALREADY = 'USER_EXISTS_ALREADY';
export const USER_DOES_NOT_EXIST = 'USER_DOES_NOT_EXIST';
export const TOKEN_IS_EMPTY = 'TOKEN_IS_EMPTY';
export const EMAIL_IS_IN_WRONG_FORMAT = 'EMAIL_IS_IN_WRONG_FORMAT';
utils.js
import sha256 from 'sha256';
import { check } from 'express-validator';
import {
PASSWORD_IS_EMPTY,
PASSWORD_LENGTH_MUST_BE_MORE_THAN_8,
EMAIL_IS_EMPTY,
EMAIL_IS_IN_WRONG_FORMAT,
} from './constant';
export const generateHashedPassword = password => sha256(password);
export function generateServerErrorCode(res, code, fullError, msg, location = 'server') {
const errors = {};
errors[location] = {
fullError,
msg,
};
return res.status(code).json({
code,
fullError,
errors,
});
}
// ================================
// Validation:
// Handle all validation check for the server
// ================================
export const registerValidation = [
check('email')
.exists()
.withMessage(EMAIL_IS_EMPTY)
.isEmail()
.withMessage(EMAIL_IS_IN_WRONG_FORMAT),
check('password')
.exists()
.withMessage(PASSWORD_IS_EMPTY)
.isLength({ min: 8 })
.withMessage(PASSWORD_LENGTH_MUST_BE_MORE_THAN_8),
];
export const loginValidation = [
check('email')
.exists()
.withMessage(EMAIL_IS_EMPTY)
.isEmail()
.withMessage(EMAIL_IS_IN_WRONG_FORMAT),
check('password')
.exists()
.withMessage(PASSWORD_IS_EMPTY)
.isLength({ min: 8 })
.withMessage(PASSWORD_LENGTH_MUST_BE_MORE_THAN_8),
];
Passport.js 설정
import { Strategy, ExtractJwt } from 'passport-jwt';
import { config, underscoreId } from './config';
import { User } from '../database/models';
export const applyPassportStrategy = passport => {
const options = {};
options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
options.secretOrKey = config.passport.secret;
passport.use(
new Strategy(options, (payload, done) => {
User.findOne({ email: payload.email }, (err, user) => {
if (err) return done(err, false);
if (user) {
return done(null, {
email: user.email,
_id: user[underscoreId]
});
}
return done(null, false);
});
})
);
};
export const config = {
passport: {
secret: '<Add_Your_Own_Secret_Key>',
expiresIn: 10000,
},
env: {
port: 8080,
mongoDBUri: 'mongodb://localhost/test',
mongoHostName: process.env.ENV === 'prod' ? 'mongodbAtlas' : 'localhost',
},
};
export const underscoreId = '_id';
passport과 함께 사용하도록 app.js를 수정합니다.
import express from 'express';
import logger from 'winston';
import bodyParser from 'body-parser';
import cors from 'cors';
import passport from 'passport';
import mongoose from 'mongoose';
import { config } from './store/config';
import { applyPassportStrategy } from './store/passport';
import { userController } from './controller';
const app = express();
// Set up CORS
app.use(cors());
// Apply strategy to passport
applyPassportStrategy(passport);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// API Route
app.use('/', userController);
/**
* Get port from environment and store in Express.
*/
const { port, mongoDBUri, mongoHostName } = config.env;
app.listen(port, () => {
logger.info(`Started successfully server at port ${port}`);
mongoose
.connect(mongoDBUri, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
logger.info(`Conneted to mongoDB at ${mongoHostName}`);
});
});
앱 실행
$ npm start
이제 다시 돌아가서 우리 API에 passport-jwt를 적용하여 user.controller.js를 개선해 보겠습니다.
등록 / 로그인 API에 Passport-jwt 적용
import express from 'express';
import jwt from 'jsonwebtoken';
import { validationResult } from 'express-validator';
import { config } from '../store/config';
import {
generateHashedPassword,
generateServerErrorCode,
registerValidation,
loginValidation,
} from '../store/utils';
import {
SOME_THING_WENT_WRONG,
USER_EXISTS_ALREADY,
WRONG_PASSWORD,
USER_DOES_NOT_EXIST,
} from '../store/constant';
import { User } from '../database/models';
const userController = express.Router();
const createUser = (email, password) => {
const data = {
email,
hashedPassword: generateHashedPassword(password),
};
return new User(data).save();
}
/**
* GET/
* retrieve and display all Users in the User Model
*/
userController.get('/', (req, res) => {
User.find({}, (err, result) => {
res.status(200).json({ data: result });
});
});
/**
* POST/
* Register a user
*/
userController.post('/register', registerValidation, async (req, res) => {
const errorsAfterValidation = validationResult(req);
if (!errorsAfterValidation.isEmpty()) {
return res.status(400).json({
code: 400,
errors: errorsAfterValidation.mapped(),
});
}
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
await createUser(email, password);
// Sign token
const newUser = await User.findOne({ email });
const token = jwt.sign({ email }, config.passport.secret, {
expiresIn: 10000000,
});
const userToReturn = { ...newUser.toJSON(), ...{ token } };
delete userToReturn.hashedPassword;
res.status(200).json(userToReturn);
} else {
generateServerErrorCode(res, 403, 'register email error', USER_EXISTS_ALREADY, 'email');
}
} catch (e) {
generateServerErrorCode(res, 500, e, SOME_THING_WENT_WRONG);
}
});
/**
* POST/
* Login a user
*/
userController.post('/login', loginValidation, (req, res) => {
const errorsAfterValidation = validationResult(req);
if (!errorsAfterValidation.isEmpty()) {
return res.status(400).json({
code: 400,
errors: errorsAfterValidation.mapped(),
});
}
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user && user.email) {
const isPasswordMatched = user.comparePassword(password);
if (isPasswordMatched) {
// Sign token
const token = jwt.sign({ email }, config.passport.secret,
{
expiresIn: 1000000,
});
const userToReturn = { ...user.toJSON(), ...{ token } };
delete userToReturn.hashedPassword;
res.status(200).json(userToReturn);
} else {
generateServerErrorCode(res, 403, 'login password error', WRONG_PASSWORD, 'password');
}
} else {
generateServerErrorCode(res, 404, 'login email error', USER_DOES_NOT_EXIST, 'email');
}
});
export default userController;
테스팅
API 등록 성공 토큰 및 사용자 이메일 + ID 반환
권한 부여
사용자가 로그인해야 하는 특정 링크로 이동 하려는지 살펴 보겠습니다. 그런 다음 API에 인증을 추가하기 만하면 됩니다.
예를 살펴 보겠습니다.
API에 연결된 JWT가없는 예
Passport JWT를 통한 승인
user.controller.js에 다음 하이라이트를 추가하세요.
import express from 'express';
import jwt from 'jsonwebtoken';
import passport from 'passport';
import { validationResult } from 'express-validator';
...
/**
* GET/
* retrieve and display all Users in the User Model
*/
userController.get(
'/',
**passport.authenticate('jwt', { session: false }),**
(req, res) => {
User.find({}, (err, result) => {
res.status(200).json({ data: result });
});
}
);
...
export default userController;
이제 Postman으로 API를 테스트하십시오. "Authorization"을 클릭하고 "Bearer Token"유형을 선택합니다. 그런 다음 토큰 필드에 토큰을 붙여 넣고 다음을 실행하십시오.
JWT를 사용하면 모든 사용자를 검색 할 수 있습니다.
이제 API를 사용하기 전에 사용자가 로그인해야 하는 다른 모든 경로를 승인하고 보호 할 수 있습니다.
등록된 댓글이 없습니다.