Angular 6我必须在客户端生成jwt以匹配服务器还是只匹配服务器
Posted
技术标签:
【中文标题】Angular 6我必须在客户端生成jwt以匹配服务器还是只匹配服务器【英文标题】:Angular 6 do I have to generate jwt in client to match server or just the server 【发布时间】:2019-06-01 04:23:33 【问题描述】:我是 jwt 的新手,感谢您的耐心等待。
我在 php 中使用以下代码生成 jwt:
// 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);
只需放在本地存储中即可。仅此而已吗?
不应该有服务器生成的token和客户端生成的token的对比吗?
如果是这样,那么我该如何翻译上面的代码呢?例如,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>
再次修改帖子以显示来自 (click)="checkRequest('/products'); 的 console.log 结果
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)
);
点击“/products”链接时来自console.log:
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" ));
在导航栏组件中,我将方法改为如下调用服务的checkRequest:
checkRequest(url)
this.srvc.checkRequest(url).subscribe(data =>
console.log("after clicking login: ", data);
);
这是控制台中的 404 错误:
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</pre>↵</body>↵</html>↵"
headers: HttpHeaders normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ
message: "Http failure response for http://localhost:4200/login: 404 Not Found"
name: "HttpErrorResponse"
ok: false
status: 404
statusText: "Not Found"
url: "http://localhost:4200/login"
怎么可能找不到 /login ?我真的在登录页面上。是不是因为在应用进入登录页面之前检查了这个?
这时我意识到我不需要发送“/login”而是将 uri 发送到 api,这没有产生错误,但标头中也没有 jwt。我尝试在 admin/products 页面上做同样的事情,以下行似乎只是返回来自 api 的有效负载:
return (this.clickResponse = this.http.get(newUrl, observe: "body" ));
然而,我在标题部分中没有看到 jwt 授权承载传递的任何内容。我错过了什么?
提前致谢
【问题讨论】:
这是一篇来自 Angular 大学的优秀文章:Angular Security - Authentication With JSON Web Tokens (JWT): The Complete Guide。它讨论了您可以在 Angular 应用程序中使用的几种不同策略。您可以将您的令牌保存到 localStorage;您也可以使用 cookie。您必须在您的应用程序和服务器之间的后续通信中使用服务器生成的令牌。 @paulsm4 感谢您的回复。我会阅读这篇文章并再次感谢您 回答您的另一个问题:客户端不需要“验证”JWT。还值得一提的是,使用 HTTP(以明文进行通信)在很大程度上违背了首先使用令牌的意义。您应该将 JWT 令牌与 HTTPS 传输结合使用。 @paulsm4 感谢您的回复。我的代码中是否有某些内容让您认为它将位于 HTTPS 域/网站上?是json_encoded数组的回声吗?如果是这样,那么我还能为 subscribe(result => this.jwtObj = result ) 做些什么来在发布请求的 Angular 6 应用程序中接收响应? 不,我是说您的服务器应该是一个 HTTPS 网站。否则,令牌很容易被劫持。在这种情况下 - 使用令牌有什么意义?实际上有一个强有力的论据,即ALL 面向 Internet 的站点应该是 HTTPS。例如:theverge.com/2018/2/8/16991254/… 【参考方案1】:以下对 app.module.ts 的导入是我所缺少的:
import HTTP_INTERCEPTORS from "@angular/common/http";
import AuthInterceptor from "./auth-interceptor";
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
在 providers 数组中拥有 HTTP_INTERCEPTORS 并使用我制作的身份验证拦截器,会自动附加到每个 http 请求 - 每个 http 请求。
此外,有时我尝试将身份验证拦截器注入登录服务,当我最终意识到我需要将服务注入我的拦截器时,通过以下调用 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);
从服务中获取令牌:
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 完成
顺便说一句,感谢您的耐心,因为我问了几个问题
【讨论】:
酷 - 很高兴你让它工作了!请务必“接受”您的解决方案。以上是关于Angular 6我必须在客户端生成jwt以匹配服务器还是只匹配服务器的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 jwt 在 Angular 6 和 web api 中刷新令牌?
服务器重新启动socket.io socketio-jwt后Angular 6验证客户端
将 JWT 存储在基于会话的 cookie Angular 6 中