天天看點

在SPA應用中利用JWT進行身份驗證在SPA應用中利用JWT進行身份驗證

版權聲明:本文為部落客chszs的原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/chszs/article/details/79639919

在SPA應用中利用JWT進行身份驗證

  • 2018.3.21
  • 版權聲明:本文為部落客chszs的原創文章,未經部落客允許不得轉載。

SPA

SPA即Single Page Application,單頁應用程式,是一種Web應用程式或網站,通過動态地重寫目前的頁面,而不是從伺服器端加載一個新網頁與使用者互動。此方法避免了連續頁面之間的使用者體驗的中斷,使應用程式更像是桌面應用程式。在SPA中,所有必需的代碼(HTML,JavaScript和CSS)都是通過單頁加載來檢索的,或者适當的資源是動态加載的并根據需要添加到頁面中,通常是響應使用者操作。盡管可以使用位置散列或HTML5 History API來提供應用程式中單獨的邏輯頁面的感覺和導航性,但頁面在該過程的任何時間點都不會重新加載,也不會将控件轉移到另一個頁面。與單頁面應用程式的互動通常涉及在背景與Web伺服器的動态通信。像Angular.js、Ember.js、Meteor.js、ExtJS、React等JS架構都采用了SPA原則。

JWT

JWT即JSON Web Token,是一個基于JSON的開放标準(RFC-7519),它用于建立通路令牌以判斷權利要求。例如,伺服器可能會生成一個令牌,其中聲明“以管理者身份登入”并将其提供給用戶端。然後用戶端可以使用該令牌來證明它以管理者身份登入。令牌由伺服器密鑰簽名,是以用戶端和伺服器都能夠驗證令牌是否合法。JWT令牌被設計成緊湊的、URL-safe的、尤其适合在Web浏覽器單點登入SSO的上下文。JWT聲明通常可用于傳遞身份提供者與服務提供者之間的身份驗證使用者身份,或業務流程所要求的任何其他類型的聲明。JWT令牌還可以被認證和加密。JWT标準還依賴于其它基于JSON的标準,比如RFC-7515的JWS(JSON Web Signature)、RFC-7516的JWE(JSON Web Encryption)。

一個成功的令牌認證系統要求使用者了解安全細節和其他認證憑證。SPA應用通常與Restful API緊密聯系在一起,将UI與所需資料進行綁定,并為UI帶來更好的外觀和感覺。但問題是,怎樣以安全的方式實作API通路?

一個這樣的SPA架構是Angular。我們可以借助基于JSON Web Token的身份驗證來驗證我們的Angular應用程式。是以,當建立Angular應用程式時,一個大問題是,您如何知道您的使用者是誰以及他們可以通路哪些内容?

針對所有這些問題的答案以及針對API的認證和授權問題的解決方案是JWT認證系統,它正在取代傳統的基于cookie的認證。這給你一個很好的方式來聲明一個使用者及其在應用程式中的通路權限。它為您提供了一個加密報頭作為令牌,以保護經身份驗證的使用者對應用程式的通路,并為您的前端和後端建構通路控制邏輯。

讓我們通過在您的Angular應用程式(版本2+)中檢視幾個驗證代碼來深入了解它。

前端工作

當使用者發出登入請求時,我們通過我們的身份驗證服務調用後端,如下所示:

login(username: string, password: string): Observable < boolean > {
    return this.http.post('/api/authenticate', JSON.stringify({
            username: username,
            password: password
        }))
        .map((response: Response) => {
            // login successful if there's a jwt token in the response
            let token = response.json() && response.json().token;
            if (token) {
                // set token property
                this.token = token;
                // store username and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('currentUser', JSON.stringify({
                    username: username,
                    token: token
                }));
                // return true to indicate successful login
                return true;
            } else {
                // return false to indicate failed login
                return false;
            }
        });
}
           

伺服器端工作

當使用者嘗試使用使用者名和密碼登入應用程式時,伺服器端将:

  • 驗證使用者
  • 生成令牌
  • 将令牌發送給使用者

後端可以是任何像Node.js這樣的伺服器端架構,Node.js使用npm的jsonwebtoken子產品來簽署和驗證JSON Web令牌。以下是一些示例代碼:

子產品

var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
           

登入功能

login: function (req, res) {
    // this is param checking if they are provided 
    if (!_.has(req.body, 'email') || !_.has(req.body, 'password')) {
        return res.serverError("No field should be empty.");
    }
    // check if the username matches any email or phoneNumber
    User.findOne({
        email: req.body.email
    }).exec(function callback(err, user) {
        if (err) return res.serverError(err);
        if (!user) return res.serverError("User not found, please sign up.");
        //check password
        bcrypt.compare(req.body.password, user.password, function (error, matched) {
            if (error) return res.serverError(error);
            if (!matched) return res.serverError("Invalid password.");
            //save the date the token was generated for already inside toJSON()
            var token = jwt.sign(user.toJSON(), "this is my secret key", {
                expiresIn: '10m'
            });
            //return the token here
            res.ok(token);
        });
    });
}
           

令牌傳回如何工作

使用者使用其憑據成功登入後,會傳回一個Web令牌,它以本地存儲方式進行儲存。現在,無論何時使用者想要通路受保護的路由,都應該在請求報頭中攜帶授權報頭項發送JWT。報頭的内容如下所示:

Authorization: Bearer 
           

是以,JWT将被用于認證和授權API,這些API将授予通路其受保護的路由和資源的權限。

使用Angular處理Web令牌

由于JWT現在儲存在本地存儲中,我們可以使用它來防止未經身份驗證的使用者通路受限制的路由。它用于路由子產品到任何受保護的路由。為此,我們使用Angular 2中的AuthGuard。AuthGuard使用了來自@angular/router的CanActivate方法。

import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate {
    constructor(private router: Router) { }
    canActivate() {
        if (localStorage.getItem('currentUser')) {
        // logged in so return true
            return true;
        }
        // not logged in so redirect to login page
        this.router.navigate(['/login']);
        return false;
    }
}
           

是以,可以像這樣在路由子產品中保護路由:

{ path: '', component: SomeComponent, canActivate: [AuthGuard] }
           

接下來,我們使用此令牌來擷取SPA應用所需的資料。它可以通路使用者的API端點。此端點将以SPA為使用者需要的資料作出響應。我們可以使用使用者服務,如下所示。

getUsers(): Observable<User[]> {
    // add authorization header with jwt token
    let headers = new Headers({ 'Authorization': 'Bearer ' + this.authenticationService.token });
    let options = new RequestOptions({ headers: headers });
    // get users from api
    return this.http.get('/api/users', options)
            .map((response: Response) => response.json());
}
           

注意我們通過使用RequestOptions實作了在報頭Header中攜帶了上面讨論的Authorization Bearer項。為此,可以使用npm中的另一個子產品,即’angular2-jwt’。它是幫助建構Angular 2應用程式的輔助工具庫。當從應用程式發出HTTP請求時,它會自動将JWT作為授權報頭附加。它使用AuthHttp類發送一個JWT請求,進而有條件地允許基于JWT狀态的路由導航。它可以按照如下所示進行安裝。

npm install angular2-jwt 
           

AuthHTTP可以在調用使用者API時使用,如下所示:

// get users from api
return this.authHttp.get('/api/users')
        .map((response: Response) => response.json());
           

最後

本文的主旨是提供一個關于JWT的概述,以及如何使用JWT來保護單頁應用程式。

繼續閱讀