您好,登錄后才能下訂單哦!
本文介紹了在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由 .
分隔的三個部分組成,它們是:
也就是說,JWT只是一個具有以下格式的字符串:
header.payload.signature
頭部
頭部通常由兩部分組成:令牌的類型(即JWT)以及正在使用的散列算法,例如HMAC SHA256或RSA。
header.payload.signature
然后,對這個JSON進行Base64編碼,形成JWT的第一部分。
ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9
荷載
JWT的第二部分是荷載,其中包含聲明。 聲明是關于實體(通常是用戶)和其他數據的聲明。聲明有三種:注冊的聲明、公開的聲明和私有的聲明。
JWT規范定義了七個在標準中注冊的聲明名稱,它們是:
對于特定情況,可以使用公共的聲明名稱。 這些包括:
最后,還有私有的聲明名稱,可以使用它們來傳達與身份相關的信息,例如姓名或部門。
由于公共和私人的聲明未注冊,請注意避免名稱沖突。
比如,我們定義一個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 {}
在 JwtModule
的 config
中:
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; }
在這個服務中,定義了兩個變量 loggedIn
和 isAdmin
,用來標識用戶是否登錄和其相應的權限,方便在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的時候需要注意以下幾點:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。