portaldacalheta.pt
  • Principal
  • Receita E Crescimento
  • Aprendendo
  • Processo Interno
  • Processo De Design
Processo Interno

Como fazer autenticação JWT com um Angular 6 SPA



Hoje veremos como é fácil integrar Token da web JSON (JWT) autenticação em seu Angular 6 (ou posterior) aplicativo de página única (SPA). Vamos começar com um pouco de fundo.

O que são JSON Web Tokens e por que usá-los?

A resposta mais fácil e concisa aqui é que eles são convenientes, compactos e seguros. Vejamos essas afirmações em detalhes:



  1. Conveniente : Usar um JWT para autenticação no back-end, uma vez conectado, requer a configuração de um cabeçalho HTTP, uma tarefa que pode ser facilmente automatizada por meio de uma função ou subclasse, como veremos mais tarde.
  2. Compactar : Um token é simplesmente uma string codificada em base64, contendo alguns campos de cabeçalho e uma carga útil, se necessário. O JWT total geralmente é inferior a 200 bytes, mesmo se assinado.
  3. Seguro : Embora não seja obrigatório, um ótimo recurso de segurança do JWT é que os tokens podem ser assinados usando criptografia de par de chaves públicas / privadas RSA ou criptografia HMAC usando um segredo compartilhado. Isso garante a origem e a validade de um token.

Tudo isso se resume a que você tem uma maneira segura e eficiente de autenticar usuários e, em seguida, verificar as chamadas para seus terminais de API sem ter que analisar nenhuma estrutura de dados nem implementar sua própria criptografia.



Teoria da Aplicação

Fluxo de dados típico para autenticação JWT e uso entre sistemas front-end e back-end



Então, com um pouco de conhecimento, agora podemos mergulhar em como isso funcionaria em um aplicativo real. Para este exemplo, vou assumir que temos um servidor Node.js hospedando nossa API, e estamos desenvolvendo um SPA toda a lista usando Angular 6. Vamos trabalhar também com esta estrutura de API:

  • /auth → POST (poste nome de usuário e senha para autenticar e receber de volta um JWT)
  • /todos → GET (retornar uma lista de itens da lista de tarefas para o usuário)
  • /todos/{id} → GET (retornar um item específico da lista de tarefas)
  • /users → GET (retorna uma lista de usuários)

Faremos a criação deste aplicativo simples em breve, mas por enquanto, vamos nos concentrar na interação em teoria. Temos uma página de login simples, onde o usuário pode inserir seu nome de usuário e senha. Quando o formulário é enviado, ele envia essas informações para o /auth ponto final. O servidor Node pode então autenticar o usuário da maneira apropriada (pesquisa de banco de dados, consulta de outro serviço da web etc.), mas, em última análise, o terminal precisa retornar um JWT.



O JWT para este exemplo conterá alguns reivindicações reservadas , e alguns reivindicações privadas . As declarações reservadas são simplesmente pares de valores-chave recomendados pelo JWT comumente usados ​​para autenticação, enquanto as declarações privadas são pares de valores-chave aplicáveis ​​apenas ao nosso aplicativo:

Reivindicações reservadas



  • iss: Emissor deste token. Normalmente o FQDN do servidor, mas pode ser qualquer coisa, desde que o aplicativo cliente saiba que o espera.
  • exp: Data e hora de expiração deste token. Isto é em segundos desde a meia-noite de 01 de janeiro de 1970 GMT (horário Unix).
  • nbf: Não é válido antes do carimbo de data / hora. Não é usado com frequência, mas fornece um limite inferior para a janela de validade. Mesmo formato que exp.

Reivindicações privadas

  • uid: ID do usuário do usuário conectado.
  • role: Função atribuída ao usuário conectado.

Nossas informações serão codificadas em base64 e assinadas usando HMAC com a chave compartilhada todo-app-super-shared-secret. Abaixo está um exemplo de como o JWT se parece:



eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b2RvYXBpIiwibmJmIjoxNDk4MTE3NjQyLCJleHAiOjE0OTgxMjEyNDIsInVpZCI6MSwicm9sZSI6ImFkbWluIn0.ZDz_1vcIlnZz64nSM28yA1s-4c_iw3Z2ZtP-SgcYRPQ

este string é tudo o que precisamos para ter certeza de que temos um login válido, para saber qual usuário está conectado e até mesmo qual (is) função (ões) o usuário tem.

A maioria das bibliotecas e aplicativos armazenam este JWT em localStorage ou sessionStorage para fácil recuperação, mas isso é apenas uma prática comum. O que você faz com o token é com você, contanto que você possa fornecê-lo para chamadas de API futuras.



Agora, sempre que o SPA quiser fazer uma chamada para qualquer um dos endpoints de API protegidos, ele simplesmente precisa enviar junto com o token no Authorization Cabeçalho HTTP.

Authorization: Bearer {JWT Token}

Nota : Mais uma vez, esta é uma prática comum. O JWT não prescreve nenhum método específico para enviar a si mesmo ao servidor. Você também pode anexá-lo ao URL ou enviá-lo em um cookie.



Depois que o servidor recebe o JWT, ele pode decodificá-lo, garantir a consistência usando o segredo compartilhado HMAC e verificar a expiração usando exp e nbf Campos. Ele também pode usar o iss campo para garantir que era a parte emissora original deste JWT.

Assim que o servidor estiver satisfeito com a validade do token, as informações armazenadas no JWT podem ser usadas. Por exemplo, o uid que incluímos nos dá o ID do usuário que fez a solicitação. Para este exemplo específico, também incluímos role , que nos permite tomar decisões sobre se o usuário deve ser capaz de acessar um determinado endpoint ou não. (Se você confia nessas informações ou deseja fazer uma pesquisa no banco de dados depende do nível de segurança necessário.)

function getTodos(jwtString) { var token = JWTDecode(jwtstring); if( Date.now() token.exp*1000) { throw new Error('Token has expired'); } if( token.iss != 'todoapi') { throw new Error('Token not issued here'); } var userID = token.uid; var todos = loadUserTodosFromDB(userID); return JSON.stringify(todos); }

Vamos construir um aplicativo de todo simples

Para acompanhar, você precisará ter uma versão recente do Node.js (6.x ou posterior), npm (3.x ou posterior) e angular-cli instalados. Se você precisar instalar o Node.js, que inclui npm, siga as instruções Aqui . Depois angular-cli pode ser instalado usando npm (ou yarn, se você o instalou):

# installation using npm npm install -g @angular/cli # installation using yarn yarn global add @angular/cli

Não vou entrar em detalhes sobre o boilerplate do Angular 6 que usaremos aqui, mas para a próxima etapa, criei um repositório Github para conter um pequeno aplicativo de tarefas para ilustrar a simplicidade de adicionar autenticação JWT ao seu aplicativo. Basta cloná-lo usando o seguinte:

git clone https://github.com/sschocke/angular-jwt-todo.git cd angular-jwt-todo git checkout pre-jwt

O git checkout pre-jwt o comando muda para uma versão nomeada onde o JWT não foi implementado.

Deve haver duas pastas dentro, chamadas server e client. O servidor é um servidor Node API que hospedará nossa API básica. O cliente é nosso aplicativo Angular 6.

O Node API Server

Para começar, instale as dependências e inicie o servidor API.

cd server # installation using npm npm install # or installation using yarn yarn node app.js

Você deve ser capaz de seguir esses links e obter uma representação JSON dos dados. Por enquanto, até que tenhamos a autenticação, codificamos o /todos endpoint para retornar as tarefas para userID=1:

  • http: // localhost: 4000 / : Página de teste para ver se o servidor Node está em execução
  • http: // localhost: 4000 / api / users : Lista de retorno de usuários no sistema
  • http: // localhost: 4000 / api / all : Retornar lista de tarefas para userID=1

The Angular App

Para começar com o aplicativo cliente, também precisamos instalar as dependências e iniciar o servidor de desenvolvimento.

cd client # using npm npm install npm start # using yarn yarn yarn start

Nota : Dependendo da velocidade da sua linha, pode demorar um pouco para baixar todas as dependências.

Se tudo estiver indo bem, você deve ver algo assim ao navegar para http: // localhost: 4200 :

A versão não habilitada para JWT de nosso aplicativo Angular Todo List.

Adicionando autenticação via JWT

Para adicionar suporte à autenticação JWT, usaremos algumas bibliotecas padrão disponíveis que a tornam mais simples. Você pode, é claro, renunciar a essas conveniências e implementar tudo sozinho, mas isso está além do nosso escopo aqui.

este tipo de equilíbrio apresenta elementos que vêm de um ponto central ou criam um foco central.

Primeiro, vamos instalar uma biblioteca no lado do cliente. É desenvolvido e mantido por Auth0 , que é uma biblioteca que permite adicionar autenticação baseada em nuvem para um site. Utilizar a biblioteca em si não requer o uso de seus serviços.

cd client # installation using npm npm install @auth0/angular-jwt # installation using yarn yarn add @auth0/angular-jwt

Chegaremos ao código em um segundo, mas enquanto estamos nisso, vamos configurar o lado do servidor também. Usaremos body-parser, jsonwebtoken e express-jwt bibliotecas para fazer o Node entender os corpos JSON POST e JWTs.

cd server # installation using npm npm install body-parser jsonwebtoken express-jwt # installation using yarn yarn add body-parser jsonwebtoken express-jwt

Endpoint de API para autenticação

Primeiro, precisamos de uma maneira de autenticar os usuários antes de fornecer um token. Para nossa demonstração simples, vamos apenas configurar um endpoint de autenticação fixo com um nome de usuário e senha embutidos em código. Isso pode ser tão simples ou tão complexo quanto seu aplicativo requer. O importante é enviar um JWT de volta.

Em server/app.js adicione uma entrada abaixo da outra require linhas da seguinte forma:

const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const expressJwt = require('express-jwt');

Bem como o seguinte:

app.use(bodyParser.json()); app.post('/api/auth', function(req, res) { const body = req.body; const user = USERS.find(user => user.username == body.username); if(!user || body.password != 'todo') return res.sendStatus(401); var token = jwt.sign({userID: user.id}, 'todo-app-super-shared-secret', {expiresIn: '2h'}); res.send({token}); });

Este é principalmente um código JavaScript básico. Obtemos o corpo JSON que foi passado para o /auth endpoint, encontre um usuário que corresponda a esse nome de usuário, verifique se temos um usuário e a senha correspondam e retorne um 401 Unauthorized Erro HTTP se não.

A parte importante é a geração de tokens, e vamos dividir isso por seus três parâmetros. A sintaxe para sign é o seguinte: jwt.sign(payload, secretOrPrivateKey, [options, callback]), onde:

  • payload é um objeto literal de pares de valores-chave que você gostaria de codificar em seu token. Essas informações podem ser decodificadas do token por qualquer pessoa que possua a chave de descriptografia. Em nosso exemplo, codificamos o user.id para que, quando recebermos o token novamente no back-end para autenticação, saibamos com qual usuário estamos lidando.
  • secretOrPrivateKey é uma chave secreta compartilhada de criptografia HMAC - isso é o que usamos em nosso aplicativo, para simplificar - ou uma chave privada de criptografia RSA / ECDSA.
  • options representa uma variedade de opções que podem ser passadas para o codificador na forma de pares de valores-chave. Normalmente, especificamos pelo menos expiresIn (torna-se exp reivindicação reservada) e issuer (iss reivindicação reservada) para que um token não seja válido para sempre e o servidor possa verificar se ele de fato emitiu o token originalmente.
  • callback é uma função a ser chamada após a codificação ser concluída, caso se deseje manipular a codificação do token de forma assíncrona.

(Você também pode ler sobre mais detalhes em options e como usar criptografia de chave pública em vez de uma chave secreta compartilhada .)

Integração Angular 6 JWT

Para fazer o Angular 6 funcionar com nosso JWT é bastante simples usando angular-jwt. Basta adicionar o seguinte a client/src/app/app.modules.ts:

import { JwtModule } from '@auth0/angular-jwt'; // ... export function tokenGetter() { return localStorage.getItem('access_token'); } @NgModule({ // ... imports: [ BrowserModule, AppRoutingModule, HttpClientModule, FormsModule, // Add this import here JwtModule.forRoot({ config: { tokenGetter: tokenGetter, whitelistedDomains: ['localhost:4000'], blacklistedRoutes: ['localhost:4000/api/auth'] } }) ], // ... }

Isso é basicamente tudo o que é necessário. Claro, temos mais alguns códigos para adicionar para fazer a autenticação inicial, mas o angular-jwt A biblioteca se encarrega de enviar o token junto com cada solicitação HTTP.

  • O tokenGetter() A função faz exatamente o que diz, mas a forma como é implementada depende inteiramente de você. Optamos por retornar o token que salvamos em localStorage. É claro que você é livre para fornecer qualquer outro método que desejar, desde que ele retorne o Token da web JSON string codificada.
  • O whiteListedDomains existe a opção para que você possa restringir para quais domínios o JWT é enviado, de forma que APIs públicas não recebam seu JWT também.
  • O blackListedRoutes opção permite que você especifique rotas específicas que não devem receber o JWT, mesmo se estiverem em um domínio na lista de permissões. Por exemplo, o endpoint de autenticação não precisa recebê-lo porque não há nenhum ponto: o token normalmente é nulo quando é chamado de qualquer maneira.

Fazendo tudo funcionar junto

Neste ponto, temos uma maneira de gerar um JWT para um determinado usuário usando o /auth endpoint em nossa API, e temos o encanamento feito no Angular para enviar um JWT com cada solicitação HTTP. Ótimo, mas você pode apontar que absolutamente nada mudou para o usuário. E você estaria correto. Ainda podemos navegar para todas as páginas em nosso aplicativo e podemos chamar qualquer endpoint de API sem nem mesmo enviar um JWT. Não é bom!

Precisamos atualizar nosso aplicativo cliente para nos preocupar com quem está conectado e também atualizar nossa API para exigir um JWT. Vamos começar.

Precisaremos de um novo componente Angular para fazer login. Para ser breve, vou manter isso o mais simples possível. Também precisaremos de um serviço que atenderá a todos os nossos requisitos de autenticação e um Guarda Angular para proteger as rotas que não devem ser acessíveis antes de fazer o login. Faremos o seguinte no contexto do aplicativo cliente.

cd client ng g component login --spec=false --inline-style ng g service auth --flat --spec=false ng g guard auth --flat --spec=false

Isso deveria ter gerado quatro novos arquivos no client pasta:

src/app/login/login.component.html src/app/login/login.component.ts src/app/auth.service.ts src/app/auth.guard.ts

Em seguida, precisamos fornecer o serviço de autenticação e proteção para nosso aplicativo. Atualizar client/src/app/app.modules.ts:

import { AuthService } from './auth.service'; import { AuthGuard } from './auth.guard'; // ... providers: [ TodoService, UserService, AuthService, AuthGuard ],

E então atualize o roteamento em client/src/app/app-routing.modules.ts para fazer uso da proteção de autenticação e fornecer uma rota para o componente de login.

// ... import { LoginComponent } from './login/login.component'; import { AuthGuard } from './auth.guard'; const routes: Routes = [ { path: 'todos', component: TodoListComponent, canActivate: [AuthGuard] }, { path: 'users', component: UserListComponent, canActivate: [AuthGuard] }, { path: 'login', component: LoginComponent}, // ...

Finalmente, atualize client/src/app/auth.guard.ts com o seguinte conteúdo:

import { Injectable } from '@angular/core'; import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) { } canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) { if (localStorage.getItem('access_token')) { return true; } this.router.navigate(['login']); return false; } }

Para nosso aplicativo de demonstração, estamos simplesmente verificando a existência de um JWT no armazenamento local. Em aplicativos do mundo real, você decodificaria o token e verificaria sua validade, expiração, etc. Por exemplo, você poderia usar JwtHelperService por esta.

Nesse ponto, nosso aplicativo Angular agora sempre redirecionará você para a página de login, pois não temos como fazer login. Vamos corrigir isso, começando com o serviço de autenticação em client/src/app/auth.service.ts:

import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable() export class AuthService { constructor(private http: HttpClient) { } login(username: string, password: string): Observable { return this.http.post('/api/auth', {username: username, password: password}) .pipe( map(result => { localStorage.setItem('access_token', result.token); return true; }) ); } logout() { localStorage.removeItem('access_token'); } public get loggedIn(): boolean { return (localStorage.getItem('access_token') !== null); } }

Nosso serviço de autenticação tem apenas duas funções, login e logout:

  • login POST s fornecido username e password ao nosso backend e define o access_token em localStorage se receber um de volta. Por razões de simplicidade, não há tratamento de erros aqui.
  • logout simplesmente limpa access_token de localStorage, exigindo que um novo token seja adquirido antes que qualquer outra coisa possa ser acessada novamente.
  • loggedIn é uma propriedade booleana que podemos usar rapidamente para determinar se o usuário está logado ou não.

E, por último, o componente de login. Eles não têm nenhuma relação com o trabalho real com o JWT, então fique à vontade para copiar e colar em client/src/app/login/login.components.html:

{{error}}

Username Password Login

E client/src/app/login/login.components.ts vai precisar:

import { Component, OnInit } from '@angular/core'; import { AuthService } from '../auth.service'; import { Router } from '@angular/router'; import { first } from 'rxjs/operators'; @Component({ selector: 'app-login', templateUrl: './login.component.html' }) export class LoginComponent { public username: string; public password: string; public error: string; constructor(private auth: AuthService, private router: Router) { } public submit() { this.auth.login(this.username, this.password) .pipe(first()) .subscribe( result => this.router.navigate(['todos']), err => this.error = 'Could not authenticate' ); } }

Pronto, nosso exemplo de login do Angular 6:

A tela de login do nosso aplicativo de amostra Angular Todo List.

Nesta fase, devemos ser capazes de fazer login (usando jemma, paul ou sebastian com a senha todo) e ver todas as telas novamente. Mas nosso aplicativo mostra os mesmos cabeçalhos de navegação e nenhuma maneira de fazer logout, independentemente do estado atual. Vamos consertar isso antes de prosseguirmos para consertar nossa API.

Em client/src/app/app.component.ts, substitua o arquivo inteiro pelo seguinte:

import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { AuthService } from './auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private auth: AuthService, private router: Router) { } logout() { this.auth.logout(); this.router.navigate(['login']); } }

E para client/src/app/app.component.html substitua a seção pelo seguinte:

Todo List Users Login Logout

Tornamos nossa navegação ciente do contexto de que ela deve exibir apenas alguns itens, dependendo se o usuário está logado ou não. auth.loggedIn pode, é claro, ser usado em qualquer lugar onde você possa importar o serviço de autenticação.

Protegendo a API

Você pode estar pensando, isso é ótimo ... tudo parece estar funcionando maravilhosamente . Mas tente fazer login com todos os três nomes de usuário diferentes e você notará algo: todos eles retornam a mesma lista de tarefas. Se dermos uma olhada em nosso servidor API, podemos ver que cada usuário, de fato, tem sua própria lista de itens, então o que há?

Bem, lembre-se de quando começamos, codificamos nosso /todos Endpoint da API para sempre retornar a lista de tarefas para userID=1. Isso acontecia porque não tínhamos como saber quem era o usuário conectado no momento.

Agora, vamos ver como é fácil proteger nossos endpoints e usar as informações codificadas no JWT para fornecer a identidade do usuário necessária. Inicialmente, adicione esta linha ao seu server/app.js arquivo logo abaixo do último app.use() ligar:

app.use(expressJwt({secret: 'todo-app-super-shared-secret'}).unless({path: ['/api/auth']}));

Usamos o express-jwt middleware, diga a ele qual é o segredo compartilhado e especifique uma matriz de caminhos para os quais ele não deve exigir um JWT. E é isso. Não há necessidade de tocar em cada ponto de extremidade, crie if todas as declarações, ou qualquer coisa.

Internamente, o middleware está fazendo algumas suposições. Por exemplo, ele assume que Authorization O cabeçalho HTTP segue o padrão JWT comum de Bearer {token}. (A biblioteca tem muitas opções para personalizar como funciona se esse não for o caso. Veja Express-jwt Usage para mais detalhes.)

Nosso segundo objetivo é usar as informações codificadas do JWT para descobrir quem está fazendo a chamada. Mais uma vez express-jwt vem ao resgate. Como parte da leitura e verificação do token, ele define a carga útil codificada que enviamos no processo de assinatura para a variável req.user no Express. Podemos então usá-lo para acessar imediatamente qualquer uma das variáveis ​​que armazenamos. Em nosso caso, definimos userID igual ao ID do usuário autenticado e, como tal, podemos usá-lo diretamente como req.user.userID.

Atualizar server/app.js novamente e altere o /todos endpoint para ler da seguinte forma:

res.send(getTodos(req.user.userID));

Nosso aplicativo Angular Todo List aproveitando o JWT para mostrar a lista de afazeres do usuário conectado, em vez daquela que codificamos anteriormente.

E é isso. Nossa API agora está protegida contra acesso não autorizado e podemos determinar com segurança quem é nosso usuário autenticado em qualquer terminal. Nosso aplicativo cliente também tem um processo de autenticação simples, e qualquer serviço HTTP que escrevermos que chame nosso endpoint de API terá automaticamente um token de autenticação anexado.

Se você clonou o repositório Github e simplesmente deseja ver o resultado final em ação, pode verificar o código em sua forma final usando:

git checkout with-jwt

Espero que você tenha achado este passo a passo valioso para adicionar Autenticação JWT para seus próprios aplicativos Angular. Obrigado por ler!

este é o nome que se refere ao espaço vazio ao redor do tipo ou outros recursos em um layout.
Relacionado: Tutorial JSON Web Token: Um Exemplo no Laravel e AngularJS

Compreender o básico

O que é um token em programação?

Em termos de programação, um token tem muitas definições. Em relação à segurança e autenticação, é simplesmente uma string opaca que codifica uma pequena quantidade de informação que pode ser facilmente transmitida e armazenada, mas com quase nenhuma chance de colisões com qualquer outro token.

O que é autenticação baseada em token?

A autenticação baseada em token significa simplesmente codificar os dados necessários para autenticar e autorizar um usuário em um token e, em seguida, usar esse token assinado criptograficamente para autorização em vez de combinações de nome de usuário / senha ao acessar recursos protegidos.

O que é JWT em Java?

JWT é uma abreviatura de JSON Web Token, o que basicamente significa que é um objeto JSON com cabeçalho, carga útil e assinatura. Ele representa uma maneira segura de trocar informações de autenticação entre duas partes em uma rede, quando usado em conjunto com outras tecnologias, como SSL.

O que é OAuth 2.0?

OAuth 2.0 é um protocolo padrão para executar a autorização em um grande número de plataformas suportadas. Por exemplo, Google, Facebook e OpenID usam OAuth 2.0. Ele define as etapas e o formato a seguir para autenticar com o servidor de autenticação fornecido e obter acesso aos recursos protegidos.

Criação de código verdadeiramente modular sem dependências

Gerenciamento De Projetos

Criação de código verdadeiramente modular sem dependências
Desmistificando criptomoedas, blockchain e ICOs

Desmistificando criptomoedas, blockchain e ICOs

Processos Financeiros

Publicações Populares
Linha de assunto - Como abordar o design de e-mail
Linha de assunto - Como abordar o design de e-mail
Gerentes de produto x Gerentes de projeto: entendendo as semelhanças e diferenças essenciais
Gerentes de produto x Gerentes de projeto: entendendo as semelhanças e diferenças essenciais
Tutorial de física de videogame - Parte II: Detecção de colisão para objetos sólidos
Tutorial de física de videogame - Parte II: Detecção de colisão para objetos sólidos
Estética e percepção - como abordar as imagens da experiência do usuário
Estética e percepção - como abordar as imagens da experiência do usuário
Escreva testes que importem: enfrente o código mais complexo primeiro
Escreva testes que importem: enfrente o código mais complexo primeiro
 
Como Facilitar a Mudança por meio da Liderança de Servo Ágil
Como Facilitar a Mudança por meio da Liderança de Servo Ágil
O lendário alpinista Jim Bridwell morre aos 73 anos
O lendário alpinista Jim Bridwell morre aos 73 anos
Tutorial Grape Gem: como construir uma API semelhante a REST em Ruby
Tutorial Grape Gem: como construir uma API semelhante a REST em Ruby
Energia híbrida: vantagens e benefícios da vibração
Energia híbrida: vantagens e benefícios da vibração
Dê a eles incentivos - como aproveitar uma estrutura de design de programa de fidelidade
Dê a eles incentivos - como aproveitar uma estrutura de design de programa de fidelidade
Publicações Populares
  • como calcular o preço da opção a partir do preço das ações
  • quão grande é a indústria de esportes eletrônicos
  • ferramentas de teste de unidade para rede
  • c # visual studio mac
  • números de cartão de crédito ativos com cvv 2017
Categorias
  • Receita E Crescimento
  • Aprendendo
  • Processo Interno
  • Processo De Design
  • © 2022 | Todos Os Direitos Reservados

    portaldacalheta.pt