国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【轉(zhuǎn)載】json web token使用

UsherChen / 1832人閱讀

摘要:當(dāng)使用一個時,其中一個挑戰(zhàn)就是認(rèn)證。在傳統(tǒng)的應(yīng)用中,服務(wù)端成功的返回一個響應(yīng)依賴于兩件事。通常包括將發(fā)送的憑證與存儲的憑證進(jìn)行檢查。第一種是使用請求來通過驗證,使服務(wù)端發(fā)送帶有的響應(yīng)。

做了這么長時間的web開發(fā),從JAVA EE中的jsf,spring,hibernate框架,到spring web MVC,到用php框架thinkPHP,到現(xiàn)在的nodejs,我自己的看法是越來越喜歡干凈整潔的web層,之前用jsf開發(fā)做view層的時候,用的primefaces做的界面顯示,雖然primefaces的確提供了很大的便利,可以讓開發(fā)人員專注于業(yè)務(wù)邏輯開發(fā),這樣其實就省去了前端開發(fā)的工作。而后來發(fā)現(xiàn)有些客戶需要的展現(xiàn)形式很難實現(xiàn),或者通過拼湊的方法實現(xiàn)的結(jié)果效率不高。使用不靈活,后來自己漸漸的轉(zhuǎn)向了做前端工程師。spring WEB MVC可以做到干凈整潔的web層,可以做到web層分離,通過ajax和服務(wù)端通信?,F(xiàn)在在學(xué)習(xí)AngularJS框架,后臺數(shù)據(jù)服務(wù)端打算用REST風(fēng)格的接口來做,這個在前后臺交互上就要考慮數(shù)據(jù)通信的安全問題,關(guān)于這個在關(guān)于SESSION的理解一文中其實有提到的。

轉(zhuǎn)載請注明出處:http://www.haomou.net/2014/08/13/2014_web_token/

來龍去脈

諸如Ember,Angular,Backbone之類的前端框架類庫正隨著更加精細(xì)的Web應(yīng)用而日益壯大。正因如此,服務(wù)器端的組建也正正在從傳統(tǒng)的任務(wù)中解脫,轉(zhuǎn)而變的更像API。API使得傳統(tǒng)的前端和后端的概念解耦。開發(fā)者可以脫離前端,獨(dú)立的開發(fā)后端,在測試上獲得更大的便利。這種途徑也使得一個移動應(yīng)用和網(wǎng)頁應(yīng)用可以使用相同的后端。

當(dāng)使用一個API時,其中一個挑戰(zhàn)就是認(rèn)證(authentication)。在傳統(tǒng)的web應(yīng)用中,服務(wù)端成功的返回一個響應(yīng)(response)依賴于兩件事。一是,他通過一種存儲機(jī)制保存了會話信息(Session)。每一個會話都有它獨(dú)特的信息(id),常常是一個長的,隨機(jī)化的字符串,它被用來讓未來的請求(Request)檢索信息。其次,包含在響應(yīng)頭(Header)里面的信息使客戶端保存了一個Cookie。服務(wù)器自動的在每個子請求里面加上了會話ID,這使得服務(wù)器可以通過檢索Session中的信息來辨別用戶。這就是傳統(tǒng)的web應(yīng)用逃避HTTP面向無連接的方法(This is how traditional web applications get around the fact that HTTP is stateless)。

API應(yīng)該被設(shè)計成無狀態(tài)的(Stateless)。這意味著沒有登陸,注銷的方法,也沒有sessions,API的設(shè)計者同樣也不能依賴Cookie,因為不能保證這些request是由瀏覽器所發(fā)出的。自然,我們需要一個新的機(jī)制。這篇文章關(guān)注于JSON Web Tokens,簡寫為JWTs,一個可能的解決這個問題的機(jī)制。這篇文章利用Node的Express框架作為后端,以及Backbone作為前端。

常用方法

第一個是使用在HTTP規(guī)范中所制定的Basic Auth, 它需要在在響應(yīng)中設(shè)定一個驗證身份的Header??蛻舳吮仨氃诿總€子響應(yīng)是附加它們的憑證(credenbtial),包括它的密碼。如果這些憑證通過了,那么用戶的信息就會被傳遞到服務(wù)端應(yīng)用。

第二個方面有點(diǎn)類似,但是使用應(yīng)用自己的驗證機(jī)制。通常包括將發(fā)送的憑證與存儲的憑證進(jìn)行檢查。和Basic Auth相比,這種需要在每次請求(call)中發(fā)送憑證。

第三種是OAuth(或者OAuth2)。為第三方的認(rèn)證所設(shè)計,但是更難配置。至少在服務(wù)器端更難。

在使用中,并不會每次都讓用戶提交用戶名和密碼,通常的情況是客戶端通過一些可靠信息和服務(wù)器交換取token,這個token作為客服端再次請求的權(quán)限鑰匙。Token通常比密碼更加長而且復(fù)雜。比如說,JWTs通常會長達(dá)150個字符。一旦獲得了token,在每次調(diào)用API的時候都要附加上它。然后,這仍然比直接發(fā)送賬戶和密碼更加安全,哪怕是HTTPS。
把token想象成一個安全的護(hù)照。你在一個安全的前臺驗證你的身份(通過你的用戶名和密碼),如果你成功驗證了自己,你就可以取得這個。當(dāng)你走進(jìn)大樓的時候(試圖從調(diào)用API獲取資源),你會被要求驗證你的護(hù)照,而不是在前臺重新驗證。

JWTs

JWTs是一份草案,盡管在本質(zhì)上它是一個老生常談的一種更加具體的認(rèn)證授權(quán)的機(jī)制。一個JWT被周期(period)分成了三個部分。JWT是URL-safe的,意味著可以用來查詢字符參數(shù)。(譯者注:也就是可以脫離URL,不用考慮URL的信息)。關(guān)于Json Web Token,參考 http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html

JWT的第一部分是對一個簡單js對象的編碼后的字符串,這個js對象是用來描述這個token類型以及使用的hash算法。下面的例子展示的是一個使用了HMAC SHA-256算法的JWT token。

{
  "typ" : "JWT",
  "alg" : "HS256"
}

在加密之后,這個對象變成了一個字符串:

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9

JWT的第二部分是token的核心,這部分同樣是對一個js對象的編碼,包含了一些摘要信息。有一些是必須的,有一些是選擇性的。實例如下:

{
  "iss": "joe",
  "exp": 1300819380,
  "http://example.com/is_root": true
}

這個結(jié)構(gòu)被稱為JWT Claims Set。這個iss是issuer的簡寫,表明請求的實體,可以是發(fā)出請求的用戶的信息。exp是expires的簡寫,是用來指定token的生命周期。(相關(guān)參數(shù)參看:the document)加密編碼之后如下:

eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ

JWT的第三個部分,是JWT根據(jù)第一部分和第二部分的簽名(Signature)。像這個樣子:

dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

最后將上面的合并起來,JWT token如下:

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

處理Tokens

我們將用JWT simple模塊去處理token,它將使我們從鉆研如何加密解密中解脫出來。如果你有興趣,可以閱讀這篇說明,或者讀這個倉庫的源碼。
首先我們將使用下面的命令安裝這個庫。記住你可以在命令中加入—save,讓其自動的讓其加入到你的package.json文件里面。

npm install jwt-simple
在你應(yīng)用的初始環(huán)節(jié),加入以下代碼。這個代碼引入了Express和JWT simple,而且創(chuàng)建了一個新的Express應(yīng)用。最后一行設(shè)定了app的一個名為jwtTokenSecret的變量,其值為‘YOUR_SECRET_STRING’(記得把它換成別的)。

var express = require("express");
var jwt = require("jwt-simple");
var app = express();

app.set("jwtTokenSecret", "YOUR_SECRET_STRING");

獲取token

我們需要做的第一件事就是讓客戶端通過他們的賬號密碼交換token。這里有2種可能的方法在RESTful API里面。第一種是使用POST請求來通過驗證,使服務(wù)端發(fā)送帶有token的響應(yīng)。除此之外,你可以使用GET請求,這需要他們使用參數(shù)提供憑證(指URL),或者更好的使用請求頭。
這篇文章的目的是為了解釋token驗證的方法而不是基本的用戶名/密碼驗證機(jī)制。所以我們假設(shè)我們已經(jīng)通過請求得到了用戶名和密碼:

User.findOne({ username: username }, function(err, user) {
  if (err) {
    // user not found
    return res.send(401);
  }

  if (!user) {
    // incorrect username
    return res.send(401);
  }

  if (!user.validPassword(password)) {
    // incorrect password
    return res.send(401);
  }

  // User has authenticated OK
  res.send(200);
});
如果用戶成功驗證賬號和密碼,然后我們生成一個token,返回給用戶。

1
var expires = moment().add("days", 7).valueOf();
var token = jwt.encode({
  iss: user.id,
  exp: expires
}, app.get("jwtTokenSecret"));

res.json({
  token : token,
  expires: expires,
  user: user.toJSON()
});

注意到j(luò)wt.encode()函數(shù)有2個參數(shù)。第一個就是一個需要加密的對象,第二個是一個加密的密鑰。這個token是由我們之前提到的iss和exp組成的。注意到Moment.js被用來設(shè)置token將在7天之后失效。而res.json()方法用來傳遞這個JSON對象給客戶端。

驗證Token

客戶端獲取到token后,應(yīng)該在每次向服務(wù)器請求數(shù)據(jù)時附帶這個token,然后服務(wù)端驗證token。
為了驗證JWT,我們需要寫出一些可以完成這些功能的中間件(Middleware):

檢查附上的token
試圖解密
驗證token的可用性
如果token是合法的,檢索里面用戶的信息,以及附加到請求的對象上
我們來寫一個中間件的框架

// @file jwtauth.js

var UserModel = require("../models/user");
var jwt = require("jwt-simple");

module.exports = function(req, res, next) {
  // code goes here
};

為了獲得最大的可擴(kuò)展性,我們允許客戶端使用一下3個方法附加我們的token:作為請求鏈接(query)的參數(shù),作為主體的參數(shù)(body),和作為請求頭(Header)的參數(shù)。對于最后一個,我們將使用Header x-access-token。

下面是我們的允許在中間件的代碼,試圖去檢索token:

var token = (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers["x-access-token"];

注意到他為了訪問req.body,我們需要首先使用express.bodyParser()中間件(譯者注,這個是Express 3.x的中間件)。
下一步,我們講解析JWT:

if (token) {
  try {
    var decoded = jwt.decode(token, app.get("jwtTokenSecret"));

    // handle token here

  } catch (err) {
    return next();
  }
} else {
  next();
}

如果解析的過程失敗,那么JWT Simple組件將會拋出一段異常。如果異常發(fā)生了,或者沒有token,我們將會調(diào)用next()來繼續(xù)處理請求。這代表喆我們無法確定用戶。如果一個合格的token合法并且被解碼,我們應(yīng)該得到2個屬性,iss包含著用戶ID以及exp包含token過期的時間戳。我們將首先處理后者,如果它過期了,我們就拒絕它:

if (decoded.exp <= Date.now()) {
  res.end("Access token has expired", 400);
}

如果token依舊合法,我們可以從中檢索出用戶信息,并且附加到請求對象里面去:

User.findOne({ _id: decoded.iss }, function(err, user) {
  req.user = user;
});

最后,將這個中間件附加到路由里面:

var jwtauth = require("./jwtauth.js");

app.get("/something", [express.bodyParser(), jwtauth], function(req, res){
  // do something
});

或者匹配一些路由

app.all("/api/*", [express.bodyParser(), jwtauth]);

客戶端請求

我們提供了一個簡單的get端去獲得一個遠(yuǎn)端的token。這非常直接了,所以我們不用糾結(jié)細(xì)節(jié),就是發(fā)起一個請求,傳遞用戶名和密碼,如果請求成功了,我們就會得到一個包含著token的響應(yīng)。

我們現(xiàn)在研究的是后續(xù)的請求。一個方法是通過JQuery的ajaxSetup()方法。這可以直接用來做Ajax請求,或者通過前端框架使用包裝過的Ajax方法。比如,假設(shè)我們將我們的請求使用window.localStorage.setItem(‘token’, ‘the-long-access-token’);放在本地存儲(Local Storage)里面,我們可以通過這種方法將token附加到請求頭里面:

var token = window.localStorage.getItem("token");

if (token) {
  $.ajaxSetup({
    headers: {
      "x-access-token": token
    }
  });
}

很簡單,但是這會劫持所有Ajax請求,如果這里有一個token在本地存儲里面。它將會附加到一個名為x-access-token的Header里面。

bear token

關(guān)于bear token,參看 RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage , 目前國內(nèi)各大網(wǎng)站都是用不同的token,也沒說必須使用bear token,只有twitter明確說明的是使用bear token。

OAuth 2.0 (RFC 6749) 定義了 Client 如何取得 Access Token 的方法。Client 可以用 Access Token 以 Resource Owner 的名義來向 Resource Server 取得 Protected Resource ,例如我 (Resource Owner) 授權(quán)一個手機(jī) App (Client) 以我 (Resource Owner) 的名義去 Facebook (Resource Server) 取得我的朋友名單 (Protected Resource)。OAuth 2.0 定義Access Token 是 Resource Server 用來認(rèn)證的唯一方式,有了這個, Resource Server 就不需要再提供其他認(rèn)證方式,例如賬號密碼。

然而在 RFC 6749 里面只定義抽象的概念,細(xì)節(jié)如 Access Token 格式、怎么傳到 Resource Server ,以及 Access Token 無效時, Resource Server 怎么處理,都沒有定義。所以在 RFC 6750 另外定義了 Bearer Token 的用法。Bearer Token 是一種 Access Token ,由 Authorization Server 在 Resource Owner 的允許下核發(fā)給 Client ,Resource Server 只要認(rèn)在這個 Token 就可以認(rèn)定 Client 已經(jīng)獲取 Resource Owner 的許可,不需要用密碼學(xué)的方式來驗證這個 Token 的真?zhèn)?。關(guān)于Token 被偷走的安全性問題,另一篇再說。

Bearer Token 的格式

Bearer XXXXXXXX

其中 XXXXXXXX 的格式為 b64token ,ABNF 的定義:

b64token = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="

寫成 Regular Expression 即是:

/[A-Za-z0-9-._~+/]+=*/

關(guān)于Bear Token還是打算另起一篇,詳細(xì)說明:Bearer Token

express-jwt實例

下面給一個具體的實例,這個例子的客戶端是web app,使用AngularJS框架。服務(wù)端使用NodeJS做的RESTful API接口,客戶端直接調(diào)用接口數(shù)據(jù),其中使用了token認(rèn)證機(jī)制。
當(dāng)用戶把他的授權(quán)信息發(fā)過來的時候, Node.js 服務(wù)檢查是否正確,然后返回一個基于用戶信息的唯一 token 。 AngularJS 應(yīng)用把 token 保存在用戶的 SessionStorage ,之后的在發(fā)送請求的時候,在請求頭里面加上包含這個 token 的 Authorization。如果 endpoint 需要確認(rèn)用戶授權(quán),服務(wù)端檢查驗證這個 token,然后如果成功了就返回數(shù)據(jù),如果失敗了返回 401 或者其它的異常。
用到的技術(shù):

AngularJS
NodeJS ( express.js, express-jwt 和 moongoose)
MongoDB
Redis (備用,用于記錄用戶退出登錄時候還沒有超時的token)
客戶端 : AngularJS 部分

首先,我們來創(chuàng)建我們的 AdminUserCtrl controller 和處理 login/logout 動作。

appControllers.controller("AdminUserCtrl", ["$scope", "$location", "$window", "UserService", "AuthenticationService",
    function AdminUserCtrl($scope, $location, $window, UserService, AuthenticationService) {
 
        //Admin User Controller (login, logout)
        $scope.logIn = function logIn(username, password) {
            if (username !== undefined && password !== undefined) {
 
                UserService.logIn(username, password).success(function(data) {
                    AuthenticationService.isLogged = true;
                    $window.sessionStorage.token = data.token;
                    $location.path("/admin");
                }).error(function(status, data) {
                    console.log(status);
                    console.log(data);
                });
            }
        }
 
        $scope.logout = function logout() {
            if (AuthenticationService.isLogged) {
                AuthenticationService.isLogged = false;
                delete $window.sessionStorage.token;
                $location.path("/");
            }
        }
    }
]);

這個 controller 用了兩個 service: UserService 和 AuthenticationService。第一個處理調(diào)用 REST api 用證書。后面一個處理用戶的認(rèn)證。它只有一個布爾值,用來表示用戶是否被授權(quán)。

appServices.factory("AuthenticationService", function() {
    var auth = {
        isLogged: false
    }
 
    return auth;
});
appServices.factory("UserService", function($http) {
    return {
        logIn: function(username, password) {
            return $http.post(options.api.base_url + "/login", {username: username, password: password});
        },
 
        logOut: function() {
 
        }
    }
});

好了,我們需要做張登陸頁面:

當(dāng)用戶發(fā)送他的信息過來,我們的 controller 把內(nèi)容發(fā)送到 Node.js 服務(wù)器,如果信息可用,我們把 AuthenticationService里面的 isLogged 設(shè)為 true。我們把從服務(wù)端發(fā)過來的 token 存起來,以便下次請求的時候使用。等講到 Node.js 的時候我們會看看怎么處理。

好了,我們要往每個請求里面追加一個特殊的頭信息了:[Authorization: Bearer ] 。為了實現(xiàn)這個需求,我們建立一個服務(wù),叫 TokenInterceptor。

appServices.factory("TokenInterceptor", function ($q, $window, AuthenticationService) {
    return {
        request: function (config) {
            config.headers = config.headers || {};
            if ($window.sessionStorage.token) {
                config.headers.Authorization = "Bearer " + $window.sessionStorage.token;
            }
            return config;
        },
 
        response: function (response) {
            return response || $q.when(response);
        }
    };
});

然后我們把這個interceptor 追加到 $httpProvider :

app.config(function ($httpProvider) {
    $httpProvider.interceptors.push("TokenInterceptor");
});

然后,我們要開始配置路由了,讓 AngularJS 知道哪些需要授權(quán),在這里,我們需要檢查用戶是否已經(jīng)被授權(quán),也就是查看 AuthenticationService 的 isLogged 值。

app.config(["$locationProvider", "$routeProvider", 
  function($location, $routeProvider) {
    $routeProvider.
        when("/", {
            templateUrl: "partials/post.list.html",
            controller: "PostListCtrl"
        }).
        when("/post/:id", {
            templateUrl: "partials/post.view.html",
            controller: "PostViewCtrl"
        }).
        when("/tag/:tagName", {
            templateUrl: "partials/post.list.html",
            controller: "PostListTagCtrl"
        }).
        when("/admin", {
            templateUrl: "partials/admin.post.list.html",
            controller: "AdminPostListCtrl",
            access: { requiredLogin: true }
        }).
        when("/admin/post/create", {
            templateUrl: "partials/admin.post.create.html",
            controller: "AdminPostCreateCtrl",
            access: { requiredLogin: true }
        }).
        when("/admin/post/edit/:id", {
            templateUrl: "partials/admin.post.edit.html",
            controller: "AdminPostEditCtrl",
            access: { requiredLogin: true }
        }).
        when("/admin/login", {
            templateUrl: "partials/admin.login.html",
            controller: "AdminUserCtrl"
        }).
        when("/admin/logout", {
            templateUrl: "partials/admin.logout.html",
            controller: "AdminUserCtrl",
            access: { requiredLogin: true }
        }).
        otherwise({
            redirectTo: "/"
        });
}]);

app.run(function($rootScope, $location, $window, AuthenticationService) {
    $rootScope.$on("$routeChangeStart", function(event, nextRoute, currentRoute) {
        //redirect only if both isLogged is false and no token is set
        if (nextRoute != null && nextRoute.access != null && nextRoute.access.requiredLogin 
            && !AuthenticationService.isLogged && !$window.sessionStorage.token) {

            $location.path("/admin/login");
        }
    });
});

服務(wù)端: Node.js + MongoDB 部分

為了在我們的 RESTful api 處理授權(quán)信息,我們要用到 express-jwt (JSON Web Token) 來生成一個唯一 Token,基于用戶的信息。以及驗證 Token。

首先,我們在 MongoDB 里面創(chuàng)建一個用戶的 Schema。我們還要創(chuàng)建調(diào)用一個中間件,在創(chuàng)建和保存用戶信息到數(shù)據(jù)庫之前,用于加密密碼。還有我們需要一個方法來解密密碼,當(dāng)收到用戶請求的時候,檢查是否在數(shù)據(jù)庫里面有匹配的。

var Schema = mongoose.Schema;
 
// User schema
var User = new Schema({
    username: { type: String, required: true, unique: true },
    password: { type: String, required: true}
});
 
// Bcrypt middleware on UserSchema
User.pre("save", function(next) {
  var user = this;
 
  if (!user.isModified("password")) return next();
 
  bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
    if (err) return next(err);
 
    bcrypt.hash(user.password, salt, function(err, hash) {
        if (err) return next(err);
        user.password = hash;
        next();
    });
  });
});
 
//Password verification
User.methods.comparePassword = function(password, cb) {
    bcrypt.compare(password, this.password, function(err, isMatch) {
        if (err) return cb(err);
        cb(isMatch);
    });
};

然后我們開始寫授權(quán)用戶和創(chuàng)建 Token 的方法:

exports.login = function(req, res) {
    var username = req.body.username || "";
    var password = req.body.password || "";
 
    if (username == "" || password == "") {
        return res.send(401);
    }
 
    db.userModel.findOne({username: username}, function (err, user) {
        if (err) {
            console.log(err);
            return res.send(401);
        }
 
        user.comparePassword(password, function(isMatch) {
            if (!isMatch) {
                console.log("Attempt failed to login with " + user.username);
                return res.send(401);
            }
 
            var token = jwt.sign(user, secret.secretToken, { expiresInMinutes: 60 });
 
            return res.json({token:token});
        });
 
    });
};

最后,我們需要把 jwt 中間件加到所有的,訪問時需要授權(quán)的路由上面:

/*
Get all published posts
*/
app.get("/post", routes.posts.list);
/*
    Get all posts
*/
app.get("/post/all", jwt({secret: secret.secretToken}), routes.posts.listAll);
 
/*
    Get an existing post. Require url
*/
app.get("/post/:id", routes.posts.read);
 
/*
    Get posts by tag
*/
app.get("/tag/:tagName", routes.posts.listByTag);
 
/*
    Login
*/
app.post("/login", routes.users.login);
 
/*
    Logout
*/
app.get("/logout", routes.users.logout);
 
/*
    Create a new post. Require data
*/
app.post("/post", jwt({secret: secret.secretToken}), routes.posts.create);
 
/*
    Update an existing post. Require id
*/
app.put("/post", jwt({secret: secret.secretToken}), routes.posts.update);
 
/*
    Delete an existing post. Require id
*/
app.delete("/post/:id", jwt({secret: secret.secretToken}), routes.posts.delete);

上面這個實例就采用了token的驗證方式構(gòu)建了api接口,但是有兩個問題需要解決:

用戶退出登錄,但是token并沒有失效,因為服務(wù)端沒有刪除這個token
token失效了,怎么辦,如果還是讓用于登錄重新獲取token,會體驗不好。應(yīng)該有token刷新機(jī)制。
使用Redis解決問題1

解決方法是:當(dāng)用戶點(diǎn)了 logout 按鈕的時候,Token 只會保存一段時間,就是你用 jsonwebtoken 登陸之后,token 有效的這段時間,我們將這個token存放在Redis中,生存時間也是jwt獲取這個token的時間。這個時間到期后,token 會被 redis 自動刪掉。最后,我們創(chuàng)建一個 nodejs 的中間件,檢查所有受限 endopoint 用的 token 是否存在 Redis 數(shù)據(jù)庫中。
NodeJS 配置 Reids

var redis = require("redis");
var redisClient = redis.createClient(6379);
 
redisClient.on("error", function (err) {
    console.log("Error " + err);
});
 
redisClient.on("connect", function () {
    console.log("Redis is ready");
});
 
exports.redis = redis;
exports.redisClient = redisClient;
然后,我們來創(chuàng)建一個方法,用來檢查提供的 token 是不是被

Token 管理和中間件

為了在 Redis 中保存 Token,我們要創(chuàng)建一個方法來拿到請求中的 Header 的 Token 參數(shù),然后把它作為 Redis 的 key 保存起來。值是什么我們不管它。

var redisClient = require("./redis_database").redisClient;
var TOKEN_EXPIRATION = 60;
var TOKEN_EXPIRATION_SEC = TOKEN_EXPIRATION * 60;
 
exports.expireToken = function(headers) {
    var token = getToken(headers);
 
    if (token != null) {
        redisClient.set(token, { is_expired: true });
        redisClient.expire(token, TOKEN_EXPIRATION_SEC);
    }
};
 
var getToken = function(headers) {
    if (headers && headers.authorization) {
        var authorization = headers.authorization;
        var part = authorization.split(" ");
 
        if (part.length == 2) {
            var token = part[1];
 
            return part[1];
        }
        else {
            return null;
        }
    }
    else {
        return null;
    }
};

然后,再創(chuàng)建一個中間件來驗證一下 token,當(dāng)用戶發(fā)起請求的時候:

// Middleware for token verification
exports.verifyToken = function (req, res, next) {
    var token = getToken(req.headers);
 
    redisClient.get(token, function (err, reply) {
        if (err) {
            console.log(err);
            return res.send(500);
        }
 
        if (reply) {
            res.send(401);
        }
        else {
            next();
        }
 
    });
};

verifyToken 這個方法,是一個中間件,用來拿到請求頭中的 token,然后在 Redis 里面查找它。如果 token 被發(fā)現(xiàn)了,我們就發(fā) HTTP 401.否則我們就繼續(xù)工作流,讓請求訪問 API。

我們要在用戶點(diǎn) logout 的時候,執(zhí)行 expireToken 方法:

exports.logout = function(req, res) {
    if (req.user) {
        tokenManager.expireToken(req.headers);
 
        delete req.user;
        return res.send(200);
    }
    else {
        return res.send(401);
    }
}

最后我們更新路由,用上新的中間件:

//Login
app.post("/user/signin", routes.users.signin);
 
//Logout
app.get("/user/logout", jwt({secret: secret.secretToken}), routes.users.logout);
 
//Get all posts
app.get("/post/all", jwt({secret: secret.secretToken}), tokenManager.verifyToken, routes.posts.listAll);
 
//Create a new post
app.post("/post", jwt({secret: secret.secretToken}), tokenManager.verifyToken , routes.posts.create);
 
//Edit the post id
app.put("/post", jwt({secret: secret.secretToken}), tokenManager.verifyToken, routes.posts.update);
 
//Delete the post id
app.delete("/post/:id", jwt({secret: secret.secretToken}), tokenManager.verifyToken, routes.posts.delete);

好了,現(xiàn)在我們每次發(fā)送請求的時候,我們都去解析 token, 然后看看是不是有效的。
這里有整個項目的源代碼

refresh token解決問題2

 appServices.factory("TokenInterceptor", function ($q, $window, $location,    AuthenticationService) {
        return {
            request: function (config) {
                config.headers = config.headers || {};
                if ($window.sessionStorage.token) {
                    config.headers.Authorization = "Bearer " +     $window.sessionStorage.token;
                }
                return config;
            },

            requestError: function(rejection) {
                return $q.reject(rejection);
            },

            /* Set Authentication.isAuthenticated to true if 200 received */
            response: function (response) {
                if (response != null && response.status == 200 && $window.sessionStorage.token && !AuthenticationService.isAuthenticated) {
                    AuthenticationService.isAuthenticated = true;
                }
                return response || $q.when(response);
            },

            /* Revoke client authentication if 401 is received */
            responseError: function(rejection) {
                if (rejection != null && rejection.status === 401 && ($window.sessionStorage.token || AuthenticationService.isAuthenticated)) {
                    delete $window.sessionStorage.token;
                    AuthenticationService.isAuthenticated = false;
                    $location.path("/admin/login");
                }

                return $q.reject(rejection);
            }
        };
    });

上面代碼中的最后一部分responseError其實就是授權(quán)失敗的部分,這里面的處理方法是返回到登錄授權(quán)頁面。
這里面考慮的方法是,如果是token超時,使用refresh_token來換取新的token。這個refresh_token,是一開始核發(fā)的時候一塊發(fā)布給客戶端的,這里就不能使用上面這個bear token了,要自己處理一下token的問題。
思路1:在user中記錄token超時時間,計算一下剩余時間,如果剩余時間比如說小于1分鐘,開始核發(fā)新的token,客戶端自動使用新的token,等退出時,就不核發(fā)新的token。

謝謝!

轉(zhuǎn)載請注明出處:http://www.haomou.net/2014/08/13/2014_web_token/

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/18826.html

相關(guān)文章

  • 使用JWT保護(hù)你的Spring Boot應(yīng)用 - Spring Security實戰(zhàn)

    摘要:創(chuàng)建應(yīng)用有很多方法去創(chuàng)建項目,官方也推薦用在線項目創(chuàng)建工具可以方便選擇你要用的組件,命令行工具當(dāng)然也可以。對于開發(fā)人員最大的好處在于可以對應(yīng)用進(jìn)行自動配置。 使用JWT保護(hù)你的Spring Boot應(yīng)用 - Spring Security實戰(zhàn) 作者 freewolf 原創(chuàng)文章轉(zhuǎn)載請標(biāo)明出處 關(guān)鍵詞 Spring Boot、OAuth 2.0、JWT、Spring Security、SS...

    wemall 評論0 收藏0
  • JWT過期,修改密碼,注銷登錄如何處理

    摘要:設(shè)置過期時間每次登錄,包括登錄都返回一個可用的給客戶端,保證合理登錄的用戶都可以用,實現(xiàn)不會過期的效果。直接將中該用戶的信息過期。下次通過登錄,會提醒失效,要重新登錄,我們重新生成一個新的給用戶,然后。。通過存儲,實現(xiàn)過期失效的問題了。 JWT 使用場景:(自己總結(jié)的,每次請求攜帶token,然后到服務(wù)端驗證token是否正確,是否過期,然后解碼出攜帶的用戶信息。服務(wù)端不需要再存儲se...

    wdzgege 評論0 收藏0
  • Spring Security

    摘要:框架具有輕便,開源的優(yōu)點(diǎn),所以本譯見構(gòu)建用戶管理微服務(wù)五使用令牌和來實現(xiàn)身份驗證往期譯見系列文章在賬號分享中持續(xù)連載,敬請查看在往期譯見系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問層和前端控制器但是忽略了對身份進(jìn)行驗證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 評論0 收藏0
  • JSON Web Token學(xué)習(xí)筆記

    摘要:一什么是為什么使用是機(jī)制的一種替代方案。這種情況下使用就會有更加方便。的數(shù)據(jù)結(jié)構(gòu)一般為三部分組成頭部一般包含簽名的算法和令牌的屬性負(fù)載實際需要傳遞的數(shù)據(jù)一般不加密,因此不要把重要信息放在里面簽名部分是對前兩部分的簽名,防止數(shù)據(jù)篡改。 一、什么是JSON Web Token? 1、為什么使用JSON Web Token JSON Web Token是cookie session機(jī)制的一種...

    cangck_X 評論0 收藏0
  • JSON WEB TOKEN(JWT)的分析

    摘要:當(dāng)達(dá)到過期時間時,需要對進(jìn)行續(xù)簽,可以定時想服務(wù)器提交請求,重新獲取來實現(xiàn)。注銷問題注銷問題當(dāng)客戶登錄的時候,需要注銷登錄會話,由于是沒有狀態(tài)的,只能在客戶端把刪除,偽造一個注銷的狀態(tài),真正的注銷只能等待過期。JSON WEB TOKEN(JWT)的分析 一般情況下,客戶的會話數(shù)據(jù)會存在文件中,或者引入redis來存儲,實現(xiàn)session的管理,但是這樣操作會存在一些問題,使用文件來存儲的時...

    hsluoyz 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<