7
respostas

Autênticação e autorização entre Angular 2 e Spring boot + spring security

Boa noite,

Estou fazendo a autenticação de usuário da minha aplicação no angular 2 utilizando JWT que consome os dados através da minha API java (Spring boot + Spring Security) .

Por referenciais utilizeis principalmente estes dois artigos:

https://cursos.alura.com.br/forum/topico-adicionando-token-em-toda-requisao-com-angular-2-solucao-27821 http://chariotsolutions.com/blog/post/angular-2-spring-boot-jwt-cors_part2/

No Postman eu consigo obter a autorização através do envio de um método POST para a API passando no header: o content-type: application/json e no corpo o usuário e a senha. Depois consigo consumir da API os jsons incluindo no header a authentication vindo do servidor.

A seguir segue meu componente login.component.ts.

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { UserService } from '../autenticacao/UserService';
import { Usuario } from '../usuario/Usuario';

@Component({
    moduleId: module.id,
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})

export class LoginComponent {
    model: any = {};
    loading = false;
    error = '';    
    private u : Usuario;

    constructor(
        private router: Router,        
        private userService: UserService) { }

    login() {
        console.log('Iniciando a autenticação');
        this.u = new Usuario(this.model.username, this.model.password);      
        this.userService.autentica(this.u.usuario, this.u.senha)
            .subscribe(result => {
                if (result === true) {
                    console.log('consegui pegar o token ... debug')                    
                } else {
                    this.error = 'Username or password is incorrect';
                    this.loading = false;
                }
            }, error => {
              this.loading = false;
              this.error = error;
            });
    }
}

Aqui é o meu component: user.services.ts

import { Injectable } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { HttpService } from './HttpService';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

@Injectable()
export class UserService {

  private url: string = 'http://localhost:8080/auth';

  private _loggedIn: BehaviorSubject<boolean> = new BehaviorSubject(false);
  public loggedIn: Observable<boolean> = this._loggedIn.asObservable();

  constructor(private http: Http) { }

  autentica(username: String, password: String) : Observable<boolean> {

    let headers = new Headers();
    headers.append('Content-Type', 'application/json');

      return this.http
                 .post(this.url, JSON.stringify({'username': username, 'password': password}), {headers: headers})      
                 .map( ( res: Response) => {
                    let token = res.json() && res.json().token;
                    if (token) {
                      localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token }));

                      return true;
                    } else {
                      return false;
                    }
                 }).catch((error:any) => Observable.throw(error.json().error || 'Server error'));                        
  }


  logout() {
    localStorage.removeItem('token');
  }

  isLoggedIn() {
    let token = localStorage.getItem('token');
    if(token){ //essa atribuição é feita para atualizar a variável e o resto do sistema ser notificado
      this._loggedIn.next(true);
    } else {
      this._loggedIn.next(false);
    }

    return this._loggedIn.getValue();
  }

}

Quando eu entro com o login e a senha e pressiono o botão para autenticar, eu obtenho duas resposta do servidor back-end ..... uma com método OPTIONS e outra do POST que vem com o AUTHORIZATION.

Mas eu não consigo adicionar o meu token no localStorage.

Se puderem me dar uma luz pois já tem um certo tempo e já procurei e refiz muitos exemplos pela net.

Desde já agradeço.

Att.

7 respostas

O console.log (token) exibe o token recebido no Angular? Você diz que não consegue, qual teste fez para verificar o valor guardado do token?

Oi Flavio,

A priori venho lhe parabenizar pelo seus cursos que são ótimos estou aprendendo muito contigo era o que eu realmente estava precisando para dar uma reciclada e sobre sua didática não tem o que falar ... sensacional vc tem o dom de ensinar ... parabéns.

Vamos lá quando tento fazer a autenticação neste código da classe UserService

autentica(username: String, password: String) : Observable<boolean> {
    let headers = new Headers();
    headers.append('Content-Type', 'application/json');

      return this.http
                 .post(this.url, JSON.stringify({'username': username, 'password': password}), {headers: headers})      
                 .map( ( res: Response) => {
                    let token = res.json() && res.json().token;
                    if (token) {
                      localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token }));

                      return true;
                    } else {
                      return false;
                    }
                 }).catch((error:any) => Observable.throw(error.json().error || 'Server error'));

  }

Ele dá o erro ainda na primeira linha do map ... e não executa a inclusão do token ao localStorage

let token = res.json() && res.json().token;

Após a execução fiz o console.log(token) e gerou este erro pois não foi criado a variável

Uncaught ReferenceError: token is not defined
    at <anonymous>:1:13

Por fim inclui na primeira linha do map desta função autentica um log do res da seguinte forma

console.log(res.headers.toJSON); // aqui retorna undefined
console.log(res.json()); // aqui retorna que undefined tb pois o corpo está vazio.

Ele dá uma resposta no res com status de ok e no res.headers quando estou debugando pelo navegador ... e no body ele está vazio.

Por fim apresento a imagem com os arquivos de autenticação sendo que o primeiro é um Request Method:OPTIONS que pelo que vi no stackoverflow é gerado pelo CORS para saber se posso requisitar uma api externa e o próximo é o Request Method:POST que me retorna o token.

Request URL:http://localhost:8080/auth
Request Method:OPTIONS
Status Code:200 
Remote Address:[::1]:8080
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Access-Control-Allow-Headers:Authorization, Content-Type
Access-Control-Allow-Max-Age:3600
Access-Control-Allow-Methods:POST, PUT, GET, DELETE
Access-Control-Allow-Origin:*
Content-Length:0
Date:Sun, 02 Jul 2017 14:13:00 GMT
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4,es;q=0.2
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:localhost:8080
Origin:http://localhost:3000
Referer:http://localhost:3000/login
User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36
Request URL:http://localhost:8080/auth
Request Method:POST
Status Code:200 
Remote Address:[::1]:8080
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Access-Control-Allow-Headers:Authorization, Content-Type
Access-Control-Allow-Max-Age:3600
Access-Control-Allow-Methods:POST, PUT, GET, DELETE
Access-Control-Allow-Origin:*
Authorization:Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG0iLCJleHAiOjE0OTk4NjQ3ODB9.3lOa7rXshaHLqA0H1sw-DL8zQ5gLhNk_Vl4ecLUKVYnvRWn7OnPvfeYUideODsg1xoEpeSOS5MUbuhz921ewwQ
Cache-Control:no-cache, no-store, max-age=0, must-revalidate
Content-Length:0
Date:Sun, 02 Jul 2017 14:13:00 GMT
Expires:0
Pragma:no-cache
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4,es;q=0.2
Connection:keep-alive
Content-Length:35
content-type:application/json
Host:localhost:8080
Origin:http://localhost:3000
Referer:http://localhost:3000/login
User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36
Request Payload
view source
{username: "adm", password: "adm"}
password
:
"adm"
username
:
"adm"

Eu estou com a resposta do servidor no navegador e não consigo carregar ela e adicionar no locaStorage.

Agradeço a ajuda de vocês.

Está um pouco confuso para mim porque você está tentando acessar o token no corpo da requisição e geralmente ele vem no header da requisição. Se você o envia no corpo tem algo de errado no seu servidor porque sem sombra de dúvida o token deveria estar presente.

Está me parecendo mais algum problema no seu Spring security do que problemas no código angular.

Da um verificada de como sua API envia o token, se é no corpo da requisição ou via header.

Talvez você tivesse mais chances de ter ajuda postando no curso de Spring security do que neste fórum. Mas vamos ver o que você tem para dizer depois dessas minhas considerações.

Fiquei curioso quanto ao meu código do backend com o Spring Security e fui revisá-lo. O meu código está semelhante ao deste artigo.

https://auth0.com/blog/securing-spring-boot-with-jwts/

Quando utilizo o Postman para testar minha API funciona perfeitamente. Eu realmente quero acessar o token através do header da requisição.

No Postman eu envio um método POST com as seguintes configurações:

URL: http://localhost:8080/auth No Headers: eu adiciono key: Content-Type: value: application/json No body eu envio o usuário e senha selecionando raw e passando o seguinte código

{ "username": "adm",
    "password": "adm"
}

Quando envio obtenho a resposta e o meu body está vazio e no headers da resposta tem o seguintes valores.

Status: Ok

Access-Control-Allow-Headers →Authorization, Content-Type
Access-Control-Allow-Max-Age →3600
Access-Control-Allow-Methods →POST, PUT, GET, DELETE
Access-Control-Allow-Origin →*
Authorization →Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG0iLCJleHAiOjE0OTk5MDIzNzh9.pTw08BBVZsUgZvC245jI875oKuFPZA-ZJG4YFfHVIFpI2w5knq34awA441Rc7BJZB7W7R_XKlIIPpj2RSlGH1g
Cache-Control →no-cache, no-store, max-age=0, must-revalidate
Content-Length →0
Date →Mon, 03 Jul 2017 00:39:38 GMT
Expires →0
Pragma →no-cache
X-Content-Type-Options →nosniff
X-Frame-Options →DENY
X-XSS-Protection →1; mode=block

Por estes testes e resultados creio que o backend (Spring Security) está funcionando corretamente.

Mas se você quer acessar pelo header na App no browser não pode acessar o token em res.json (). Tem que ler do header.

Veja seu código

return this.http
                 .post(this.url, JSON.stringify({'username': username, 'password': password}), {headers: headers})      
                 .map( ( res: Response) => {
                    let token = res.json() && res.json().token;
                    if (token) {
                      localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token }));

                      return true;
                    } else {
                      return false;
                    }
                 }).catch((error:any) => Observable.throw(error.json().error || 'Server error'));

  }

Por isso seu token vem vazio. Tem que se entender com sua API.

Da uma olhada só

https://stackoverflow.com/questions/36423388/how-to-get-value-in-response-header-angular2

Obrigado pela resposta mas com este link que me passou também não funcionou. Vou abrir um tópico no curso do spring boot para saber dos colegas se fiz algo errado em minha API.

Puxa...nao consigo enxergar outro problema. Tente isso sim. Mas o lance de reenviar o token sempre você está fazendo, né?