Angular 6 мне нужно сгенерировать jwt в клиенте, чтобы соответствовать серверу или только серверу - PullRequest
0 голосов
/ 06 января 2019

Я новичок в JWT, и я благодарю вас за ваше терпение.

Я генерирую jwt в PHP со следующим:

// Create token header as a JSON string
$header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);

// Create token payload as a JSON string
$payload = json_encode(['username' => $this->username, 'password' => $this->password]);

// Encode Header to Base64Url String
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));

// Encode Payload to Base64Url String
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

// Create Signature Hash
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, '<big secret>', true);

// Encode Signature to Base64Url String
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));

Все это было просто для генерации подписи - $ base64UrlSignature - которая затем устанавливается на ключ:

$ this-> key = $ base64UrlSignature;

        $token = array(
            "iss" => $this->iss,
            "aud" => $this->aud,
            "iat" => $this->iat,
            "nbf" => $this->nbf,
            "data" => array(
                "username" => $this->username,
                "password" => $this->password
                )
        );

        // generate jwt
        $this->jwt = JWT::encode($token, $this->key);
        echo json_encode(
            array(
                "status" => 401,
                "id" => 0,
                "uName" => "Guest",
                "isAdmin" => "0",
                "ts" => "2018-12-28 00:00:00",
                "loggedIn" => false,
                "msg" => "Combination of username and password not found",
                "jwt" => $this->jwt
            )
        );

У меня вопрос: нужно ли делать то же самое на стороне клиента в Angular, чтобы посмотреть, совпадает ли это с тем, что генерируется сервером?

Пока все, что я прочитал, связано с сервером, генерирующим jwt, тогда в Angular сделайте следующее:

localStorage.setItem("jwt", res.jwt);

Просто поместите в локальное хранилище. Это все, что нужно?

Не должно ли быть сравнение токена, сгенерированного сервером, с токеном, сгенерированным клиентом?

Если так, то как мне перевести приведенный выше код для этого? Например, что в Angular эквивалентно классу Google jwt PHP:

$this->jwt = JWT::encode($token, $this->key);

Я добавляю код в исходное сообщение. Вот перехватчик, который я сделал:

import { ShoppingCartValuesService } from "./shopping-cart-values.service";
import { Injectable, Injector } from "@angular/core";
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest
} from "@angular/common/http";
import { Observable } from "rxjs/";
import * as jwt_decode from "jwt-decode";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(public srvc: ShoppingCartValuesService) {}
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    const idToken = localStorage.getItem("jwt");

    if (idToken) {
      const cloned = req.clone({
        headers: req.headers.set("Authorization", "Bearer " + idToken)
      });
      console.log("Decoded jwt token: ", this.getDecodedAccessToken(idToken));
      return next.handle(cloned);
    } else {
      return next.handle(req);
    }
  }

  getDecodedAccessToken(token: string): any {
    try {
      return jwt_decode(token);
    } catch (error) {
      console.log("error: ", error);
      return false;
    }
  }
}

Который вызывается каждый раз, когда в ссылках навигационной панели делается щелчок с помощью обработчика щелчка checkRequest (), который проходит маршрут и позже добавляет весь URL:

  <li class="nav-item">
    <a
      class="nav-link"
      data-toggle="tooltip"
      title="Products"
      routerLink="/products"
      id="products"
      (click)="checkRequest('/products')"
      ><span>Products</span></a
    >
  </li>

Еще раз изменив сообщение, чтобы отобразить результат console.log из (click) = "checkRequest ('/ products');

Метод CheckRequest:

checkRequest(url) {
const newUrl = this.srvc.serverBase + url;
this.clickResponse = this.http.get(newUrl, { observe: "body" });
console.log(
  "newUrl: " +
    newUrl +
    " this.clickResponse: " +
    JSON.stringify(this.clickResponse)
);

}

Из console.log при нажатии на ссылку «/ products»:

newUrl: http://local.kronus:8001/products this.clickResponse: {"_isScalar":false,"source":{"_isScalar":false,"source":{"_isScalar":false,"source":{"_isScalar":true,"value":{"url":"http://local.kronus:8001/products","body":null,"reportProgress":false,"withCredentials":false,"responseType":"json","method":"GET","headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"params":{"updates":null,"cloneFrom":null,"encoder":{},"map":null},"urlWithParams":"http://local.kronus:8001/products"}},"operator":{"concurrent":1}},"operator":{}},"operator":{}}

Перехватчик, похоже, ничего не производит. Я что-то упустил, чтобы включить перехватчик?

Еще одно обновление: после некоторого прочтения я понял, что мне нужно ввести перехватчик в мою службу входа в систему:

private authInt: AuthInterceptor

Также я добавил в сервис входа в систему:

clickResponse: Observable<any>;

Я переместил метод checkRequest в службу входа в систему:

checkRequest(url) {
  const newUrl = this.appBase + url;
  return (this.clickResponse = this.http.get(newUrl, { observe: "body" }));
}

В компоненте navbar я изменил метод на следующий, вызывая checkRequest службы:

checkRequest(url) {
this.srvc.checkRequest(url).subscribe(data => {
  console.log("after clicking login: ", data);
});
}

Вот ошибка 404 в консоли:

<code>    HttpErrorResponse {headers: HttpHeaders, status: 404, statusText: "Not Found", url: "http://localhost:4200/login", ok: false, …}
error: "<!DOCTYPE html>↵<html lang="en">↵<head>↵<meta charset="utf-8">↵<title>Error</title>↵</head>↵<body>↵<pre>Cannot GET /login
↵ ↵ ↵» заголовки: HttpHeaders {normalizedNames: Map (0), lazyUpdate: null, lazyInit: ƒ} сообщение: «Http-сообщение об ошибке для http://localhost:4200/login: 404 Not Found» имя: "HttpErrorResponse" хорошо: ложь статус: 404 statusText: "Not Found" URL: "http://localhost:4200/login"

Как это возможно, что / логин не найден? Я буквально на странице входа. Это потому, что это проверено до того, как приложение войдет в страницу входа?

Это когда я понял, что мне нужно отправлять не '/ login', а uri к api, который не выдал ошибок, но также не указал jwt в заголовках. Я попытался сделать то же самое на странице admin / products, которая, кажется, в следующей строке просто возвращает полезную нагрузку, полученную от API:

return (this.clickResponse = this.http.get(newUrl, { observe: "body" }));

Тем не менее, я не вижу в разделе заголовков ничего, что передавал бы jwt Authorization Bearer. Чего мне не хватает?

Заранее спасибо

1 Ответ

0 голосов
/ 08 января 2019

Мне не хватает следующего импорта в app.module.ts:

import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { AuthInterceptor } from "./auth-interceptor";

{
  provide: HTTP_INTERCEPTORS,
  useClass: AuthInterceptor,
  multi: true
}

Наличие HTTP_INTERCEPTORS в массиве провайдеров и использование созданного мной перехватчика авторизации автоматически присоединяется к каждому http-запросу - КАЖДОМУ http-запросу.

Кроме того, время от времени я пытался внедрить auth перехватчик в службы входа в систему, когда в конце концов понял, что мне нужно добавить службы в мой перехватчик, с помощью следующего вызова this.srvc.getToken ():

if (idToken) {
  const cloned = req.clone({
    headers: req.headers.set(
      "Authorization",
      "Bearer " + this.srvc.getToken()
    )
  });
  // console.log("Decoded jwt token: ", this.getDecodedAccessToken(idToken));
  return next.handle(cloned);
} else {
  return next.handle(req);
}

getToken от службы:

public getToken(): string {
  return localStorage.getItem("jwt");
}

Теперь мои заголовки запросов выглядят следующим образом:

Provisional headers are shown
Accept: application/json, text/plain, */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3Q6NDIwMCIsImF1ZCI6Imh0dHA6XC9cL2xvY2FsaG9zdDo0MjAwIiwiaWF0IjoxNTQ2OTU5NzUzLCJuYmYiOjE1NDY5NTg3NTMsImRhdGEiOnsibmFtZSI6InRhbUBrcm9udXNwcm9kdWN0aW9ucy5jb20iLCJwYXNzMSI6ImVhYmNkMTIzNDUifX0.GF1kFxdMe3Jd_paxu89Dve23ysguz4LGxXmGIDOz9Yc
Content-Type: application/json
Origin: http://localhost:4200
Referer: http://localhost:4200/login
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36

Поскольку эти тесты выполняются в моей среде разработки, я не буду работать по протоколу https, но @ paulsm4 правильно, что все это нужно делать через https

Кстати, спасибо за ваше терпение, когда я задал несколько вопросов

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...