노드의 단계별 안내서를 설정합니다.js API 및 Passport JWT


인증과 권한 수여는 응용 프로그램의 중요한 구성 부분이다.보호나 검사가 없는 API 루트가 존재하면 응용 프로그램은 해커의 목표가 되기 쉽다.이것이 바로 우리가 왜 안전 팻말을 필요로 하는가 하는 것이다 — JSON Web Token (JWT).

JWT의 기초 지식


나는 JWT에 대해 깊이 있게 토론하지는 않겠지만, 여기에는 모든 기초 지식이 있다.

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.


JSON 웹 영패는 사용자의 정보를 인코딩하고 디코딩합니다.그것들은 권한 수여와 정보 교환에 사용된다.
그것들은 세 부분으로 구성되어 있다 — 제목, 유효 하중 및 서명 — 점(.으로 구분: xxxxx.yyyyy.zzzzzJSON 웹 토큰에 대한 자세한 내용 here 을 참조하십시오.

시작하기 전에


나는 너의 컴퓨터에 이미 npm와 테스트와 우편배달원이 있다고 가정한다. 만약 이것을 검사하지 않았다면.
이것은 code입니다. 만약 당신이 이 과정에서 어떤 문제가 있다면, 만약 당신이 어떤 문제를 물어볼 필요가 있다면, 트리빈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

상수회사 명

  • 우선, 상수 중에서 내가 즐겨 하는 일이 하나 있다.js 파일.나는 많은 문자열을 작성하지 않았고, 반복적으로 사용할 수 있는 문자열을 위해 변수를 만들었다.
  • TextEditor 문자열의 맞춤법 오류를 자동으로 완성하고 줄일 수 있습니다.
  • 파일에 추가:
  • 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';
    

    상수회사 명

  • 전체 프로젝트에 사용되는 모든 기능과 검증된 파일을 저장합니다.
  • API 컨트롤러 파일의 코드를 더욱 깨끗하게 합니다.
  • 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),
    ];
    

    여권.js 설정

  • 노드.인증을 위한 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);
          });
        })
      );
    };
    
  • utils.js 애플리케이션의 모든 구성을 저장하는 곳입니다.
  • 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';
    
    수정store/passport.js을passport와 함께 사용합니다.
    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에 store/config.js 를 적용하여 개선합시다 app.js.

    등록/로그인 API에 Passport jwt 적용



    이미지 출처: dotnettricks.일반 도메인 이름 형식
    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;
    
  • 사용자의 전자 우편과 해시 비밀번호를 사용하지 않고 권한을 부여하는 것은 클라이언트와 서버 간의 통신 기간에 안전하지 않을 수 있습니다.
  • 우리는 JWT 영패를 사용하여 권한을 부여한다.이렇게 하면 우리는 암호와 사용자의 전자메일이 암호화되는 안전성을 확보할 수 있다.
  • 테스트

  • 이 점에서 나는 네가 우체부를 어떻게 사용하는지 알고 있다고 가정한다.
  • 방법을 사용하고 user.controller.jspassport-jwt을 입력합니다.
  • 레지스터 API를 테스트하면 다음과 같은 결과를 얻을 수 있습니다.영패를 클립보드에 복사합니다.

  • 등록 API가 토큰과 사용자의 전자 메일 +id를 반환했습니다.

    승인


    사용자 로그인이 필요한 특정 링크로 이동하는지 살펴보겠습니다.그리고 간단하게 API에 라이센스를 추가할 수 있습니다.
    우리 예를 하나 봅시다.
  • POST/ 에는 모든 사용자의 목록을 검색하는 간단한 localhost:8080/register API가 포함되어 있습니다. — 나는 사용자 신분으로 로그인하지 않는 한 모든 사용자를 검색하고 싶지 않다.
  • 의 또 다른 예는 페이스북이다.뉴스 요약으로 넘어가서 모든 게시물을 검색하려면 로그인해야 합니다.
  • 다음은 JWT 토큰이 없는 상태에서 보안 API 라우팅으로 이동할 때(로그인하지 않은 경우라고도 함):

  • API가 연결되지 않은 JWT의 예

    JWT 여권 승인


    이 하이라이트를 추가합니다localhost:8080/login.
    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를 테스트합니다.권한 부여를 클릭한 다음 유형 적재 영패 선택그런 다음 영패 필드에 영패를 붙여넣고 실행합니다.

    JWT를 사용하면 모든 사용자를 검색할 수 있습니다

    잘했어!


    이제 API를 사용하기 전에 다른 사용자가 로그인해야 하는 모든 라우트를 승인하고 보호할 수 있습니다.
  • 를 통해 연락
  • 좋은 웹페이지 즐겨찾기