使用 ember-simple-auth 實現 Ember.js 應用的權限控制

不少網站都有登陸功能,對於Ember的應用咱們怎麼實現權限的控制呢?本篇將爲你演示一個最經常使用的權限控制例子:用戶登陸。javascript

要實現登陸最經常使用的方式是經過判斷session值,若是session中存在你所須要的值則能夠認爲是用戶是通過了登陸而且把用戶信息設置到session了,若是session中沒有用戶信息則認爲用戶沒有登陸,直接跳轉到登陸或者註冊頁面。html

本篇會引入一個專門用於控制權限的插件ember-simple-auth,文章中大部分代碼是直接參考這個插件的文檔所寫。若是你須要項目的代碼請移步github下載。java

好了,廢話少說,直接放碼出來吧。node

建立Ember應用

本文會使用Ember CLI名稱建立項目和項目所需的文件,更多有關Ember CLI的命令請自行到官網學習。git

ember new chapter8_simple_auth
cd chapter8_simple_auth
ember server

若是你的項目搭建成功執行http://localhost:4200,會看到Welcome to Ember,說明項目搭建成功。github

升級Ember、Jquery版本

本項目會升級Ember版本,目前(_2015-11-18_)來講若是是使用Ember CLI命令安裝的項目Ember的版本是1.13.7。升級後使用的版本是2.0.0-beta.3web

升級步驟:ajax

  1. 修改bower.json
    修改後此文件主要的代碼以下:shell

    {
      "dependencies": {
        "ember": "2.0.0-beta.3"
      },
          "resolutions": {
          "ember": "2.0.0-beta.3"
      }
    }
  2. 刪除原有的Ember
    必需要手動刪除原有的版本,不然由於緩存的問題使用命令從新安裝的時候可能安裝不成功。手動刪除以下目錄:appName/bower_components/embernpm

  3. 安裝新版本Ember
    使用命令:bower install,從新安裝Ember。

  4. 檢查是否安裝成功。
    打開appName/bower_components/ember/ember.js,能夠看到Ember是那個版本。若是是2.0.0-beta.3說明升級成功。

  5. 一樣的方式升級Jquery
    若是你升級不成功,你能夠參考個人項目的bower.jsonpackage.json升級。修改這兩個文件後執行命令bower install升級。

  6. 重啓項目
    能夠看到瀏覽器控制檯打印出Ember的版本信息。

    2015-11-18 23:54:08.902 ember.debug.js:5202 DEBUG: -------------------------------
    2015-11-18 23:54:08.916 ember.debug.js:5202 DEBUG: Ember             : 2.0.0-beta.3
    2015-11-18 23:54:08.916 ember.debug.js:5202 DEBUG: jQuery            : 2.1.4
    2015-11-18 23:54:08.917 ember.debug.js:5202 DEBUG: -------------------------------

到此項目的升級工做完成。

安裝插件ember-simple-auth

直接使用npm命令安裝,安裝的方法能夠參考官方教程,直接在項目目錄下運行ember install ember-simple-auth便可完成安裝。能夠在appName/bower_components看到安裝的插件。

項目主要代碼文件

首頁模板文件

{{! app/templates/application.hbs }}

<h2 id="title">This is my first auth proj</h2>

{{#if session.isAuthenticated}}
    <p>
        <a {{action 'invalidateSession'}} style="cursor: pointer;">Logout</a>
    </p>
{{else}}
    <p>
        <a {{action 'sessionRequiresAuthentication'}} style="cursor: pointer;">Login</a>
    </p>
{{/if}}

{{outlet}}

session.isAuthenticated是插件ember-simple-auth封裝好的屬性,若是沒有登陸isAuthenticated爲false,sessionRequiresAuthentication也是插件ember-simple-auth提供的方法。此方法會自動根據用戶實現的authenticate方法校驗用戶是否已經登陸(isAuthenticatedtrue)。

定義登陸、登陸成功組件

使用Ember CLI建立兩個組件:login-formget-quotes

ember g component login-form
ember g component get-quotes

分別編寫這兩個組件和組件對應的模板文件。

login-form.js

// app/components/login-form.js

import Ember from 'ember';

export default Ember.Component.extend({
    // authenticator: 'authenticator: custom',

    actions: {
        authenticate: function() {
            var user = this.getProperties('identification', 'password');
            this.get('session').authenticate('authenticator:custom', user).catch((msg) => {
                this.set('errorMessage', msg);
            });
        }
    }
});

其中authenticator屬性執行了一個自定義的身份驗證器customidentificationpassword是頁面輸入的用戶名和密碼。

getProperties方法會自動獲取屬性值並自動組裝成hash形式({key: value}形式)。msg是方法authenticate驗證不經過的提示信息。

在此簡單處理,直接放回到界面顯示。

login-form.hbs

{{! app/templates/login-form.hbs }}

<form {{action 'authenticate' on='submit'}}>
    <div class="form-group">
        <label for="identification">Login</label>
        {{input value=identification placeholder="enter your name" class="form-control"}}
    </div>
    <div class="form-group">
        <label for="password">Login</label>
        {{input value=password placeholder="enter password" class="form-control" type="password"}}
    </div>
    <button type="submit" class="btn btn-default">Login</button>
</form>
{{#if errorMessage}}
    <div class="alert alert-danger">
        <strong>Login failed: </strong>{{errorMessage}}
    </div>
{{/if}}

這個文件比較簡單沒什麼好說,errorMessage就是組件類中返回的提示信息。

get-quotes.js

// app/components/get-quotes.js

import Ember from 'ember';

export default Ember.Component.extend({
    gotQuote: false,
    quote: "",

    actions: {
        getQuote: function() {
            var that = this;
            //  返回一個隨機的字符串
            Ember.$.ajax({
                type: 'GET',
                // url: 'http://localhost:3001/api/protected/random-quote',
                url: 'http://localhost:3001/api/random-quote',
                success: function(response) {
                    that.setProperties({ quote: response, gotQuote: true });
                },
                error: function(error) {
                    alert("An error occurred while processing the response.");
                    console.log(error);
                }
            });
        }
    }
});

此組件模擬登錄以後才能訪問的資源,經過Ajax獲取一個隨機的字符串。
請求的服務代碼你也能夠從github上下載,下載以後按照文檔安裝,直接運行node server.js既可,服務器端是一個nodejs程序,若是你的電腦沒有安裝nodejs,請自行下載安裝。

登錄、信息顯示頁面

這兩個頁面比較簡單,直接調用組件。爲何我沒有直接把組件代碼放在這兩個頁面呢??咱們知道Ember2.0以後官方不推薦使用控制器,控制器的做用在弱化,組件變得愈來愈重要。

既然咱們項目使用的是Ember2.0版本那就必需要用組件去替代控制器實現某些邏輯的判斷。

{{! app/templates/login.hbs }}

{{login-form}}
{{! app/templates/protected.hbs }}

{{get-quotes}}

登錄前的提示信息

咱們直接把登錄使用的用戶名和密碼提示出來,爲了測試方便嘛,再者項目尚未註冊功能。

{{! app/templates/index.hbs }}

{{#unless session.isAuthenticated}}
    <div class="alert alert-info">
        You can {{#link-to 'login' className="alert-link"}}log in {{/link-to}} with login <code>ember</code> and password <code>123</code>.
    </div>
{{/unless}}

可是用戶名和密碼爲何是ember123呢??你看到服務器代碼裏的user-routes.js就明白了,github上用的是gonto,我下載以後作了點小修改。你能夠修改爲你喜歡的字符串。

到此常規的文件就建立完成了,下面的內容纔是重頭戲。到目前爲止咱們還沒使用過任何有關插件ember-simple-auth的內容。

路由配置

ember g route login
ember g route protected
ember g route application

執行命令的時候要注意別把以前的模板覆蓋了!!!下面是這幾個文件的內容。

application.js

// app/routes/application.js

import Ember from 'ember';

import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';

export default Ember.Route.extend(ApplicationRouteMixin, {
    actions: {
        invalidateSession: function() {
            this.get('session').invalidate();
        }
    }
});

這個類首先混合了ApplicationRouteMixin類的特性,而後再加上自定義的特性。注意第二行代碼,引入了插件ember-simple-auth的類ApplicationRouteMixin。更多有關這個類的介紹請點擊連接查看。session是插件內置的屬性。方法invalidate設置session爲無效或者說是當前認證無效,更多詳細信息請看方法的API介紹。

protected.js

// app/routes/protected.js

import Ember from 'ember';

import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';

// 實現AuthenticatedRouteMixin的類會自動根據權限過濾,若是通過登陸頁面直接進入這個route會自動跳轉到登陸頁面
export default Ember.Route.extend(AuthenticatedRouteMixin, {
});

此類也是引入插件ember-simple-auth封裝好的類AuthenticatedRouteMixin。混合了此類的類會自動根據權限過濾,若是沒有經過認證而直接訪問這個route會被強制跳轉到登陸頁面。

login.js

// app/routes/login.js

import Ember from 'ember';

export default Ember.Route.extend({
    //  清空提示信息
    setupController: function(controller, model) {
        console.log("route:login model = " + model);
        controller.set('errorMessage', null);
    }
});

這個route的做用是清空頁面的提示信息,若是不清空你再次進入的時候仍是會看到提示信息。

控制器配置

路由protected之因此能實現無權限重定向到登陸頁面是由於在controller:login中指定了登陸處理類。

login.js

// app/controllers/login.js

import Ember from 'ember';

import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';

export default Ember.Controller.extend(LoginControllerMixin, {
});

此類引入插件封裝好的登陸處理類LoginControllerMixin,遺憾的是在插件目錄下並無發現這個類,看不到裏面的實現!

核心處理類

最後的這兩個類是整個項目最核心的東西——自定義校驗器、受權者。

受權者類 authorizer/custom.js

// app/authenrizers/custom.js

import Ember from 'ember';
import Base from 'simple-auth/authorizers/base';

export default Base.extend({
    authorize: function(jqXHR, requestOptions) {
        var accessToken = this.get('session.content.secure.token');
        if (this.get('session.isAuthenticated') && !Ember.isEmpty(accessToken)) {
            //  setRequestHeader方法自定義請求頭信息:鍵爲Authorization,值爲Ember+accessToken
            // 有關這個方法的介紹請看[API介紹](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader)
            jqXHR.setRequestHeader('Authorization', 'Ember' + accessToken);
        }
    }
});

直接繼承Base類,從新實現authorize方法。或者你亦能夠像github上的教程使用插件已經定義好的類。
authorize方法第一個參數是須要設置的session數據,第二個參數是一個回調函數,更多詳情狀況接口API

驗證器類 authenticators/custom.js

//  app/authenticators/custom.js

import Ember from 'ember';
import Base from 'simple-auth/authenticators/base';

export default Base.extend({
    tokenEndpoint: 'http://localhost:3001/sessions/create',
    restore: function(data) {
        return new Ember.RSVP.Promise(function(resolve, reject) {
            if (!Ember.isEmpty(data.token)) {
                resolve(data);
            } else {
                reject();
            }
        });
    },
    authenticate: function(options) {
        return new Ember.RSVP.Promise((resolve, reject) => {
            Ember.$.ajax({
                url: this.tokenEndpoint,
                type: 'POST',
                data: JSON.stringify({
                    username: options.identification,
                    password: options.password
                }),
                contentType: 'application/json;charset=utf-8',
                dataType: 'json'
            }).then(function(response) {
                Ember.run(function() {
                    resolve({
                        token: response.id_token
                    });
                });
            }, function(xhr, status, error) {
                var response = xhr.responseText;
                Ember.run(function() {
                    reject(response);
                });
            });
        });
    },
    invalidate: function() {
        console.log('invalidate...');
        return Ember.RSVP.resolve();
    }
});

這個類代碼比較多,也比較複雜。目前官方提供了三種經常使用的驗證器。

可是本項目使用的自定義的驗證器。須要注意的是自定義的驗證器須要實現restoreauthenticateinvalidate這個三個方法,最後一個方法不強制要求重寫,可是前面兩個方法必須重寫。從代碼實現能夠看到這幾個方法都返回了Promise對象。

代碼首先是執行了Ajax請求http://localhost:3001/sessions/create,若是執行成功則返回token,不然返回出錯信息,返回的錯誤信息能夠在user-routes.js上看到,下載代碼後你能夠修改爲本身喜歡的提示信息。

修改項目配置

到此項目的主要代碼都已實現了,下面爲了項目能正常運行還須要修改項目的配置文件config/environment.js

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    // ……與原文件同樣
    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    },
    contentSecurityPolicy: {
        'default-src': "'none'",
        'script-src': "'self'",
        'font-src': "'self' *",
        'connect-src': "'self' *", // Allow data (ajax/websocket) from http://localhost:3001
        'img-src': "'self'",
        'style-src': "'self' 'unsafe-inline' *", // Allow inline styles
        'media-src': "'self'"
    }
  };
  ENV['simple-auth'] = {
        store: 'simple-auth-session-store:local-storage',
        authorizer: 'authorizer:custom',
        crossOriginWhitelist: ['http://localhost:3001/'],  // Ajax跨域設置
        // routeAfterAuthentication: '/',  //登陸成功後跳轉到的頁面
        authenticationRoute: 'login'  //  登陸不成功轉回登陸頁面
  };
  // ……與原文件同樣

  return ENV;
};

沒有列出的代碼與默認生成的代碼是一致的。

最後重啓項目測試效果。

首先咱們直接訪問 http://localhost:4200/protected,能夠看到直接被重定向到http://localhost:4200/login(前提是你還沒登錄過)。而後再訪問 http://localhost:4200 進入到項目首頁。能夠看到提示登錄的用戶名和密碼。而後點擊login轉到登錄界面。

下面是演示效果

  1. 沒有輸入用戶、密碼
    若是沒有輸入用戶名或者密碼其中之一,或者都不輸入就點擊login,會出現如圖提示信息。你也能夠看瀏覽器控制檯打印的日誌信息,能夠看到返回的狀態碼爲400,這個狀態碼也是在user-routes.js中設置的。 圖1-1

  2. 用戶名和密碼不匹配
    圖1-2

  3. 登錄成功的狀況
    圖1-3

能夠看到瀏覽器URL轉到http://localhost:4200/protected。而後點擊按鈕"Get Random quote",能夠看到返回隨機的字符串。

圖1-4

每點擊一次就發送一次請求http://localhost:3001/api/random-quote,請求返回一個隨機的字符串。

到此,使用插件ember-simple-auth實現ember應用的權限控制的內容所有結束完畢,各位讀者們不知道大家是否看得明白,若是以爲文章將不對的地方歡迎給我留言,若是你以爲做者大半夜寫文章精神可嘉也歡迎給我點個贊吧 =_=!!

參考文章

  1. https://github.com/simplabs/ember-simple-auth

  2. http://ember-simple-auth.com/api/index.html

  3. http://www.programwitherik.com/ember-simple-auth-torii-example-application/

  4. https://auth0.com/blog/2015/06/26/auth0-ember-simple-auth/

若是發現鏈接沒法訪問,那麼你可能須要fanqiang

相關文章
相關標籤/搜索