Node.js + Express Logging, Autenticação e Autorização Bruno Catão
Antes de tudo • Para organizar ainda mais nosso projeto • Vamos adicionar o script de inicialização no package.json • "start": "nodemon src/main.js“ • Para executar é só: • npm start
Executando npm start > aula3@1.0.0 start C:Usersbrunodevtap2019.1aula3 > nodemon src/main.js [nodemon] 1.18.10 [nodemon] to restart at any time, enter `rs` [nodemon] watching: *.* [nodemon] starting `node src/main.js` body-parser deprecated undefined extended:... Aplicação no ar em http://localhost:3000
Logging de Requisições • Vamos utilizar o pacote Morgan • https://github.com/expressjs/morgan • Como será que instala ? • npm i morgan --save • Como usar ? • Importar o módulo: • const morgan = require('morgan') • Escolher o formato: combined, common, dev, short, tiny • app.use(morgan('combined’)) • É só isso!
Morgan • Formatos predefinidos: • combined • :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" • common • :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] • dev • :method :url :status :response-time ms - :res[content-length] • short • :remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms • tiny • :method :url :status :res[content-length] - :response-time ms
Morgan • Por padrão, os logs são mostrados na saída padrão • Para redirecionar os logs para um arquivo: • É preciso importar os módulos fs (file system) e path • const fs = require('fs') • const path = require('path’) • Depois, cria-se um stream de saída para o arquivo de logging: • var logStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a’ }) • Configura o Morgan: • app.use(morgan('combined', { stream: logStream }))
src/main.js
Saída ::1 - GET / HTTP/1.1 200 293 - 59.417 ms ::1 - GET /images/logo.jpg HTTP/1.1 304 - - 3.229 ms ::1 - GET /css/styles.css HTTP/1.1 304 - - 4.025 ms ::1 - GET /favicon.ico HTTP/1.1 200 3 - 1.049 ms ::1 - GET /livros HTTP/1.1 304 - - 1.093 ms ::1 - GET /favicon.ico HTTP/1.1 304 - - 0.700 ms
Logging de Código • Existem vários pacotes • Os mais usados são: • Debug – Pacote simples, baseado em workspaces • Winston – Pacote voltado para aplicações, contendo níveis de logging • Trace – Logging para aplicações distribuídas, contendo microservices
Debug • Instalação: • npm i debug --save • Uso: • const debug = require('debug')(‘testes’); • debug(‘produto = %o’, produto); • Executando: • DEBUG=testes node app.js • Vantagens: • Bom para testes (debugging) • Na execução normal nenhuma saída é mostrada • É possível utilizar vários workspaces para exibir saídas diferentes
Debug • Sintaxe: debug(‘mensagem %fmt1 %fmt2’, valor1, valor2); • Formatadores: • %O – Objeto (em várias linhas) • %o – Objeto (em uma única linha) • %s - String • %d - Número • %j - JSON • %% - Caractere de porcentagem (%)
Winston • Logging de eventos da aplicação • Suporte a vários níveis de logging • Instalação: • npm i winston --save • Uso: • const winston = require('winston’); • winston.log('info', 'Hello log files!’, {someKey: 'some-value’}); • Sintaxe: • winston.log(NIVEL, MENSAGEM, VALORES);
Winston • Níveis de Severidade (RFC5424): • error: 0 • warn: 1 • info: 2 • verbose: 3 • debug: 4 • silly: 5 • Um logger exibe mensagens de um nível menor ou igual ao seu • Ex1. um logger com nível error exibe apenas mensagens error • Ex2. um logger com nível silly exibe mensagens de todos os níveis
Winston • Criando um logger: const logger = winston.createLogger({ level: 'info', transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'meuslogs.log' }) ] });
Winston • Criando um logger: const logger = winston.createLogger({ level: 'silly', transports: [ new winston.transports.Console({ level: 'silly' }), new winston.transports.File({level: 'error' , filename: 'erros.log' }), new winston.transports.File({filename: 'combinado.log' }), ] });
Winston • Criando um logger: const logger = winston.createLogger({ level: 'info', transports: [ new winston.transports.File({level: 'error' , filename: 'erros.log' }), new winston.transports.File({filename: 'combinado.log' }), ] }); if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console()); }
No nosso projeto • Vamos criar uma pasta config • Dentro dela, vamos criar um script "logger.js" • Esse script irá criar e configurar um logger, esse objeto poderá ser utilizado em qualquer ponto da aplicação que precise registrar o que está acontecendo
config/logger.js
Exemplo de uso
Saída ::1 - POST /livros HTTP/1.1 200 55 - 300.231 ms ::1 - GET /livros HTTP/1.1 200 57 - 1.747 ms {"message":"Procurando o indice do livro com id 0","level":"info"} ::1 - GET /livros/0 HTTP/1.1 200 55 - 5.824 ms ::1 - GET /favicon.ico HTTP/1.1 304 - - 1.058 ms {"message":"Procurando o indice do livro com id 3","level":"info"} {"message":"Nao foi encontrado livro com id 3","level":"error"} ::1 - GET /livros/3 HTTP/1.1 200 - - 2.386 ms ::1 - GET /favicon.ico HTTP/1.1 304 - - 1.034 ms
Saída
Trace (PM2) • Ferramenta paga • Provê visualizações dos logs (organiza em árvores, cria gráficos, etc) • Emite notificações (ex. em caso de exceções)
Trace (PM2)
JWT (JSON Web Tokens)
JWT – Como funciona ? 1. Você faz login 1. Se o login estiver correto, status 200, retorna um objeto com o token 2. Se o login estiver errado, status 500, retorna mensagem de erro 2. Tenta acessar uma URL segura 1. Verifica se no cabeçalho tem um atributo Authorization no formato: Authorization: Bearer <token> 2. Se não tiver, status 401, mensagem de que não tem o token 3. Se tiver o token, tenta verificar ele 1. Não foi possível verificar o token, status 400, mensagem de erro 2. Token verificado, adiciona o payload do token no request e permite a entrada
Lista de códigos de status HTTP • 200 – Sucesso • 400 – Requisição inválida • 401 – Não autorizado • 402 – Pagamento necessário • 403 – Proibido • 404 – Não encontrado • 500 – Erro interno do servidor
Implementando autenticação com JWT • Primeiro passo, instalar o pacote jsonwebtoken: • npm i jsonwebtoken --save • Na pasta config, vamos criar um script "constantes.js" • Vamos criar na pasta controllers um script "autorizacao.js" • Vamos criar uma pasta "util" e um script "seguranca.js" • Por fim, vamos dizer quais rotas de "livros.js" são seguras ou não.
config/constantes.js
controllers/autorizacao.js
main.js
util/seguranca.js
controllers/livros.js
Testando - Login
Testando – Rota aberta
Testando – Rota segura
Testando – Rota segura
Para a próxima aula • Valendo pontos na primeira avaliação • O projeto valerá 5 pontos • Os outros 5 pontos restantes serão um somatório das atividades propostas em sala • Com base nos exemplos das últimas aulas, faça um sistema com Node e Express que: • possua pelos menos dois perfis de usuário (ex. ADMIN e USER); • tenha rotas públicas, rotas que todos os perfis autenticados possam acessar e todas que apenas um perfil possa acessar (dica, use os dados do payload); • armazena os dados de um recurso e dos usuários em um arquivo.

Node JS - Parte 3

  • 1.
    Node.js + Express Logging,Autenticação e Autorização Bruno Catão
  • 2.
    Antes de tudo •Para organizar ainda mais nosso projeto • Vamos adicionar o script de inicialização no package.json • "start": "nodemon src/main.js“ • Para executar é só: • npm start
  • 3.
    Executando npm start > aula3@1.0.0start C:Usersbrunodevtap2019.1aula3 > nodemon src/main.js [nodemon] 1.18.10 [nodemon] to restart at any time, enter `rs` [nodemon] watching: *.* [nodemon] starting `node src/main.js` body-parser deprecated undefined extended:... Aplicação no ar em http://localhost:3000
  • 4.
    Logging de Requisições •Vamos utilizar o pacote Morgan • https://github.com/expressjs/morgan • Como será que instala ? • npm i morgan --save • Como usar ? • Importar o módulo: • const morgan = require('morgan') • Escolher o formato: combined, common, dev, short, tiny • app.use(morgan('combined’)) • É só isso!
  • 5.
    Morgan • Formatos predefinidos: •combined • :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" • common • :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] • dev • :method :url :status :response-time ms - :res[content-length] • short • :remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms • tiny • :method :url :status :res[content-length] - :response-time ms
  • 6.
    Morgan • Por padrão,os logs são mostrados na saída padrão • Para redirecionar os logs para um arquivo: • É preciso importar os módulos fs (file system) e path • const fs = require('fs') • const path = require('path’) • Depois, cria-se um stream de saída para o arquivo de logging: • var logStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a’ }) • Configura o Morgan: • app.use(morgan('combined', { stream: logStream }))
  • 7.
  • 8.
    Saída ::1 - GET/ HTTP/1.1 200 293 - 59.417 ms ::1 - GET /images/logo.jpg HTTP/1.1 304 - - 3.229 ms ::1 - GET /css/styles.css HTTP/1.1 304 - - 4.025 ms ::1 - GET /favicon.ico HTTP/1.1 200 3 - 1.049 ms ::1 - GET /livros HTTP/1.1 304 - - 1.093 ms ::1 - GET /favicon.ico HTTP/1.1 304 - - 0.700 ms
  • 9.
    Logging de Código •Existem vários pacotes • Os mais usados são: • Debug – Pacote simples, baseado em workspaces • Winston – Pacote voltado para aplicações, contendo níveis de logging • Trace – Logging para aplicações distribuídas, contendo microservices
  • 10.
    Debug • Instalação: • npmi debug --save • Uso: • const debug = require('debug')(‘testes’); • debug(‘produto = %o’, produto); • Executando: • DEBUG=testes node app.js • Vantagens: • Bom para testes (debugging) • Na execução normal nenhuma saída é mostrada • É possível utilizar vários workspaces para exibir saídas diferentes
  • 11.
    Debug • Sintaxe: debug(‘mensagem%fmt1 %fmt2’, valor1, valor2); • Formatadores: • %O – Objeto (em várias linhas) • %o – Objeto (em uma única linha) • %s - String • %d - Número • %j - JSON • %% - Caractere de porcentagem (%)
  • 12.
    Winston • Logging deeventos da aplicação • Suporte a vários níveis de logging • Instalação: • npm i winston --save • Uso: • const winston = require('winston’); • winston.log('info', 'Hello log files!’, {someKey: 'some-value’}); • Sintaxe: • winston.log(NIVEL, MENSAGEM, VALORES);
  • 13.
    Winston • Níveis deSeveridade (RFC5424): • error: 0 • warn: 1 • info: 2 • verbose: 3 • debug: 4 • silly: 5 • Um logger exibe mensagens de um nível menor ou igual ao seu • Ex1. um logger com nível error exibe apenas mensagens error • Ex2. um logger com nível silly exibe mensagens de todos os níveis
  • 14.
    Winston • Criando umlogger: const logger = winston.createLogger({ level: 'info', transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'meuslogs.log' }) ] });
  • 15.
    Winston • Criando umlogger: const logger = winston.createLogger({ level: 'silly', transports: [ new winston.transports.Console({ level: 'silly' }), new winston.transports.File({level: 'error' , filename: 'erros.log' }), new winston.transports.File({filename: 'combinado.log' }), ] });
  • 16.
    Winston • Criando umlogger: const logger = winston.createLogger({ level: 'info', transports: [ new winston.transports.File({level: 'error' , filename: 'erros.log' }), new winston.transports.File({filename: 'combinado.log' }), ] }); if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console()); }
  • 17.
    No nosso projeto •Vamos criar uma pasta config • Dentro dela, vamos criar um script "logger.js" • Esse script irá criar e configurar um logger, esse objeto poderá ser utilizado em qualquer ponto da aplicação que precise registrar o que está acontecendo
  • 18.
  • 19.
  • 20.
    Saída ::1 - POST/livros HTTP/1.1 200 55 - 300.231 ms ::1 - GET /livros HTTP/1.1 200 57 - 1.747 ms {"message":"Procurando o indice do livro com id 0","level":"info"} ::1 - GET /livros/0 HTTP/1.1 200 55 - 5.824 ms ::1 - GET /favicon.ico HTTP/1.1 304 - - 1.058 ms {"message":"Procurando o indice do livro com id 3","level":"info"} {"message":"Nao foi encontrado livro com id 3","level":"error"} ::1 - GET /livros/3 HTTP/1.1 200 - - 2.386 ms ::1 - GET /favicon.ico HTTP/1.1 304 - - 1.034 ms
  • 21.
  • 22.
    Trace (PM2) • Ferramentapaga • Provê visualizações dos logs (organiza em árvores, cria gráficos, etc) • Emite notificações (ex. em caso de exceções)
  • 23.
  • 24.
  • 25.
    JWT – Comofunciona ? 1. Você faz login 1. Se o login estiver correto, status 200, retorna um objeto com o token 2. Se o login estiver errado, status 500, retorna mensagem de erro 2. Tenta acessar uma URL segura 1. Verifica se no cabeçalho tem um atributo Authorization no formato: Authorization: Bearer <token> 2. Se não tiver, status 401, mensagem de que não tem o token 3. Se tiver o token, tenta verificar ele 1. Não foi possível verificar o token, status 400, mensagem de erro 2. Token verificado, adiciona o payload do token no request e permite a entrada
  • 26.
    Lista de códigosde status HTTP • 200 – Sucesso • 400 – Requisição inválida • 401 – Não autorizado • 402 – Pagamento necessário • 403 – Proibido • 404 – Não encontrado • 500 – Erro interno do servidor
  • 27.
    Implementando autenticação comJWT • Primeiro passo, instalar o pacote jsonwebtoken: • npm i jsonwebtoken --save • Na pasta config, vamos criar um script "constantes.js" • Vamos criar na pasta controllers um script "autorizacao.js" • Vamos criar uma pasta "util" e um script "seguranca.js" • Por fim, vamos dizer quais rotas de "livros.js" são seguras ou não.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
    Para a próximaaula • Valendo pontos na primeira avaliação • O projeto valerá 5 pontos • Os outros 5 pontos restantes serão um somatório das atividades propostas em sala • Com base nos exemplos das últimas aulas, faça um sistema com Node e Express que: • possua pelos menos dois perfis de usuário (ex. ADMIN e USER); • tenha rotas públicas, rotas que todos os perfis autenticados possam acessar e todas que apenas um perfil possa acessar (dica, use os dados do payload); • armazena os dados de um recurso e dos usuários em um arquivo.