国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

在Angular中使用JWT認證方法示例

2019-11-19 12:58:56
字體:
來源:轉載
供稿:網友

本文介紹了在Angular中使用JWT認證方法示例,分享給大家,具體如下:

項目地址: grading-system

基于session的認證和基于token的認證的方式已經被廣泛使用。在session認證中,服務端會存儲一份用戶登錄信息,這份登錄信息會在響應時傳遞給瀏覽器并保存為Cookie,在下次請求時,會帶上這份登錄信息,這樣就能識別請求來自哪個用戶。

在基于session的認證中,每個用戶都要生成一份session,這份session通常保存在內存中,隨著用戶量的增加,服務端的開銷會增大,而且對分布式應用不是很友好。

在token認證中,服務端不需要保留用戶認證信息。當用戶登錄時,服務器驗證用戶信息后會返回一個token,這個token存儲在客戶端,并且在每次請求的請求頭中都帶上這個token,這樣服務端驗證token后就可以返回數據。

JWT(JSON Web Token)是一個開放標準(RFC 7519),它定義了一種緊湊且獨立的方式,可以在各方之間作為JSON對象安全地傳輸信息。 此信息可以通過數字簽名進行驗證和信任。特別適用于分布式站點的單點登錄(SSO)場景。

JWT 是什么,為何要使用 JWT?

JWT 是 JSON Web Tokens 的簡稱,對于這個問題最精簡的回答是,JWT 具有簡便、緊湊、安全的特點,具體來看:

簡便:只要用戶登陸后,使用 JWT 認證僅需要添加一個 http header 認證信息,這可以用一個函數簡單實現,我們會在后面的例子中看到這一點。

緊湊:JWT token 是一個 base 64 編碼的字符串,包含若干頭部信息及一些必要的數據,非常簡單。簽名后的 JWT 字符串通常不超過 200 字節。

安全:JWT 可以使用 RSA 或 HMAC 加密算法進行加密,確保 token 有效且防止篡改。

總之你可以有一種安全有效的方式來認證用戶,并且對所有 api 調用都進行認證,而不需要解析復雜的數據結構或者實現自己的加密算法。

JWT的構成

JWT由 . 分隔的三個部分組成,它們是:

  • 頭部(Header)
  • 荷載(Playload)
  • 簽名(Signature)

也就是說,JWT只是一個具有以下格式的字符串:

header.payload.signature

頭部

頭部通常由兩部分組成:令牌的類型(即JWT)以及正在使用的散列算法,例如HMAC SHA256或RSA。

header.payload.signature

然后,對這個JSON進行Base64編碼,形成JWT的第一部分。

ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9

荷載

JWT的第二部分是荷載,其中包含聲明。 聲明是關于實體(通常是用戶)和其他數據的聲明。聲明有三種:注冊的聲明、公開的聲明和私有的聲明。

JWT規范定義了七個在標準中注冊的聲明名稱,它們是:

  • iss: JWT簽發者
  • sub: JWT所面向的用戶
  • aud:接收JWT的一方
  • exp:JWT的過期時間,這個過期時間必須要大于簽發時間
  • nbf:定義在什么時間之前,該JWT都是不可用的.
  • iat: JWT的簽發時間
  • jti: JWT的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。

對于特定情況,可以使用公共的聲明名稱。 這些包括:

  • auth_time:身份驗證發生的時間
  • acr:認證上下文類的引用
  • nonce:用于將客戶端會話與ID Token關聯的值

最后,還有私有的聲明名稱,可以使用它們來傳達與身份相關的信息,例如姓名或部門。

由于公共和私人的聲明未注冊,請注意避免名稱沖突。

比如,我們定義一個palyload:

{ "sub": "1234567890", "name": "tc9011", "admin": true, "exp": 1441594722}

然后將其進行base64加密,得到JWT的第二部分:

ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAidGM5MDExIiwKICAiYWRtaW4iOiB0cnVlLAogICJleHAiOiAxNDQxNTk0NzIyCn0=

簽名

簽名由base64編碼后的頭、base64編碼后的荷載和secret組成。

例如,將上面的兩個編碼后的字符串都用句號 . 連接在一起(頭部在前),就形成了:

ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAidGM5MDExIiwKICAiYWRtaW4iOiB0cnVlLAogICJleHAiOiAxNDQxNTk0NzIyCn0=

然后,將上面拼接完的字符串用secret作為秘鑰進行HS256加密。

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

使用JWT

一般在會在請求頭中加入 Authorization ,并加上 Bearer 進行標注:

fetch('api/v1/user/1', { headers: {  'Authorization': 'Bearer ' + token }})

服務端會驗證token,如果驗證通過就會返回相應的資源。

不過要注意,因為荷載是base64編碼,這種編碼可以對稱解密,所以在荷載中不應該存放用戶的敏感信息,比如密碼。所以一般JWT用來向Web傳遞一些非敏感信息,例如用戶名、所屬部門等。

在Angular中使用JWT

這里我們以Angular6和koa2(使用TypeScript)為例,介紹一下如何在你的Angular應用中使用JWT。

服務端

首先在jwt.io 官網上找到node的JWT的庫: jsonwebtoken

可以看到官網把這個庫對標準注冊聲明字段的支持情況以及加密方式的支持情況都列出來了。除了這個庫,還需要使用koa一個中間件: koa-jwt ,用來對HTTP請求進行JWT認證。你可以通過下面命令安裝這兩個庫:

npm i koa-jwt jsonwebtoken --save

app.ts 中:

import * as jwt from 'koa-jwt';app.use(jwt({   secret: Secret  }).unless({   path: [///register/, ///login/, ///groups/],  }));

這里的secret就是你自己定義的秘鑰, unless 方法用來排除一些不需要進行JWT認證的api。koa-jwt中間件需要放在路由中間件之前,這樣就可以對所有路由(除了 unless 中設置的路由外)進行JWT的檢查。只有正確之后才能正確的訪問。

除此之外,你還要自定義一個401錯誤處理的中間件,如果沒有token,或者token失效,該中間件會給出對應的錯誤信息。如果沒有自定義中間件的話,會直接將 koa-jwt 暴露的錯誤信息直接返回給用戶。

export const errorHandle = (ctx, next) => { return next().catch((err) => {  if (err.status === 401) {   ctx.status = 401;   handleError({ctx, message: '登錄過期,請重新登錄', err: err.originalError ? err.originalError.message : err.message});  } else {   throw err;  } });};

然后把這個中間件放在koa-jwt之前:

app.use(errorHandle);app.use(jwt({  secret: Secret}).unless({  path: [///register/, ///login/, ///groups/],}));

在用戶登陸時候,生成token,返回給客戶端:

// 生成 token 返回給客戶端const token = jsonwebtoken.sign({  user: {    workNumber: user.workNumber,    realName: user.realName,    group: user.group,    role: user.role  },  // 設置 token 過期時間  exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24),  // 1天}, Secret);handleSuccess({  ctx,  message: '登陸成功!',  response: {    token,    lifeTime: Math.floor(Date.now() / 1000) + (60 * 60 * 24)  // 1天  }});

需要注意的是,在使用 jsonwebtoken.sign() 時,需要傳入的 secret 參數,這里的 secret 必須要與 前面設置 jwt() 中的 secret 一致。

客戶端

在Angular中,我們需要使用 @auth0/angular2-jwt 這個庫來幫助我們在Angular中處理JWT:

npm install @auth0/angular-jwt --save

app.module.ts 中引入 JwtModule 這個模塊(注意,引入該模塊的同時也要引入 HttpClientModule 模塊):

import { JwtModule } from '@auth0/angular-jwt';import { HttpClientModule } from '@angular/common/http';export function tokenGetter(){ return localStorage.getItem('token');}@NgModule({ bootstrap: [AppComponent], imports: [  // ...  HttpClientModule,  JwtModule.forRoot({   config: {    tokenGetter: tokenGetter,    whitelistedDomains: ['localhost:3001'],    blacklistedRoutes: ['localhost:3001/auth/']   }  }) ]})export class AppModule {}

JwtModuleconfig 中:

tokenGetter :從localStorage中獲取token;

whitelistedDomains :允許發送認證的請求的域名;

blacklistedRoutes :你不希望替換header中 Authorization 信息的api列表。

接著創建一個全局的 auth.service.ts 服務,方便在登陸的時候獲取用戶相關信息及權限,這個服務中有個 login 方法,用來處理登陸后返回的token信息,并把token存到LocalStorage中,這樣在token失效前,下次用戶登陸時就不需要輸入用戶名和密碼:

login(loginInfo: LoginInfo): Observable<boolean> {  return this.passportService.postLogin(loginInfo).pipe(map(    (res: LoginRes) => {      // 登陸成功后獲取token,并存到localStorage      this.storageService.setLocalStorage('token', res.token);      const decodedUser = this.decodeUserFromToken(res.token);      this.setCurrentUser(decodedUser);      this.msg.success('登錄成功!');      return this.loggedIn;    }  )                             );}

在這個 login 方法中, decodeUserFromToken 封裝了 @auth0/angular2-jwt 中提供的 decodeToken 方法,注意 decodeToken 方法解析出來的只是服務端 jsonwebtoken.sign() 中的JSON對象,所以需要通過 . 操作獲取 jsonwebtoken.sign() 中定義的 user

decodeUserFromToken(token): User {  return this.jwtHelperService.decodeToken(token).user;}

在這個服務中,定義了兩個變量 loggedInisAdmin ,用來標識用戶是否登錄和其相應的權限,方便在Angular路由中控制可以訪問的視圖。

有登錄當然就有登出,登出時只需把token從LocalStorage中移除,并把幾個變量重置即可:

logout(): void {  this.storageService.removeLocalStorage('token');  this.loggedIn = false;  this.isAdmin = false;  this.currentUser = new User();}

AuthService 的完整代碼如下:

import { Injectable, Injector } from '@angular/core';import { Router } from '@angular/router';import { JwtHelperService } from '@auth0/angular-jwt';import { Observable } from 'rxjs';import { map } from 'rxjs/operators';import { LoginInfo, LoginRes, User } from '../../views/passport/interfaces/passport';import { PassportService } from '../../views/passport/services/passport.service';import { StorageService } from '../storage/storage.service';import { NzMessageService } from 'ng-zorro-antd';@Injectable()export class AuthService { public loggedIn = false; public isAdmin = false; public currentUser: User = new User(); constructor(private jwtHelperService: JwtHelperService,       private router: Router,       private injector: Injector,       private passportService: PassportService,       private storageService: StorageService) {  const token = localStorage.getItem('token');  if (token) {   const decodedUser = this.decodeUserFromToken(token);   this.setCurrentUser(decodedUser);  } } get msg(): NzMessageService {  return this.injector.get(NzMessageService); } login(loginInfo: LoginInfo): Observable<boolean> {  return this.passportService.postLogin(loginInfo).pipe(map(   (res: LoginRes) => {     this.storageService.setLocalStorage('token', res.token);     const decodedUser = this.decodeUserFromToken(res.token);     this.setCurrentUser(decodedUser);     this.msg.success('登錄成功!');     return this.loggedIn;    }   )  ); } logout(): void {  this.storageService.removeLocalStorage('token');  this.loggedIn = false;  this.isAdmin = false;  this.currentUser = new User(); } decodeUserFromToken(token): User {  return this.jwtHelperService.decodeToken(token).user; } setCurrentUser(decodedUser): void {  this.loggedIn = true;  this.currentUser.workNumber = decodedUser.workNumber;  this.currentUser.realName = decodedUser.realName;  this.currentUser.group = decodedUser.group;  this.currentUser.role = decodedUser.role;  this.isAdmin = decodedUser.role > 10;  delete decodedUser.role; }}

至此,在你的Angular應用中就引入了JWT認證,當然,你也可以不使用 @auth0/angular2-jwt ,自己手寫一個HTTP攔截器,手動設置每次請求的header:

@Injectable()export class AuthInterceptor implements HttpInterceptor {  intercept(req: HttpRequest<any>,       next: HttpHandler): Observable<HttpEvent<any>> {    const token = localStorage.getItem("token");    if (token) {      const cloned = req.clone({        headers: req.headers.set("Authorization",          "Bearer " + token)      });      return next.handle(cloned);    }    else {      return next.handle(req);    }  }}

不過這樣的話,token Base64解碼也需要自己手寫,稍微麻煩一點。

總結

JWT因為是基于JSON的,所以通用性很強,很多語言已經存在jwt相關的庫。不過使用JWT的時候需要注意以下幾點:

  • 保存好secret秘鑰,這個秘鑰只能在服務端存在
  • 給token設置一個過期時間,因為一旦token生成,它就永遠有效,除非token密鑰被更改或過期
  • 在payload中只能存儲一些業務邏輯所必要的非敏感信息

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沽源县| 隆子县| 庆阳市| 密山市| 雷波县| 淮阳县| 上饶市| 广宗县| 巴楚县| 长宁区| 平阴县| 昔阳县| 阳城县| 潜山县| 两当县| 南通市| 双桥区| 巩留县| 奇台县| 常德市| 尖扎县| 文安县| 搜索| 台南县| 昌邑市| 耒阳市| 鄂州市| 清镇市| 南川市| 密山市| 泗洪县| 新沂市| 胶南市| 望江县| 长宁区| 本溪| 古浪县| 嘉义市| 饶平县| 尖扎县| 兴化市|