AngularJs實現(xiàn)基于角色的前端訪問控制

Github 項目地址 https://github.com/zgljl2012/angular-permission

最近做的項目是使用Angular做一個單頁應用,但因為用戶有不同的角色(管理員秒拔、編輯挑围、普通財務人員等)手幢,所以需要進行不同角色的訪問控制哩掺。

因為后端訪問控制的經(jīng)驗比較豐富云芦,所以這里只記錄了前端訪問控制的實現(xiàn)关炼。請注意程腹,前端最多只能做到顯示控制!并不能保證安全儒拂,所以后端是一定要做訪問控制的寸潦!

基于角色的訪問控制需要做到兩個層面的訪問控制:

  1. 控制頁面路由的跳轉(zhuǎn),沒有權限的用戶不能跳轉(zhuǎn)到指定url
  2. 頁面元素的顯示控制社痛,沒有對應權限的用戶不能看到該元素

但在此之前见转,我們還有一項重要的事要做。

存儲用戶信息

首先我們要做的蒜哀,并不是和訪問控制有關的事斩箫,首先我們要保存好用戶信息。包括用戶的基本信息撵儿,如用戶名乘客、真實姓名;以及用戶角色淀歇。下面是數(shù)據(jù)結(jié)構(gòu):

user = {
  username:"",
  realname:"",
  role:""
}

存儲的時候就將整個user存儲易核,但存在哪里呢?考慮到必須在任何頁面都可以訪問到浪默,第一反應是存儲到$rootScope中牡直,但我們應該盡量避免使用$rootScope;除此之外浴鸿,我們可以存儲在頂級的controller或者是全局的constant中井氢,這兩種解決方案都可以,但它們的問題就是一旦頁面刷新岳链,就不管用了($rootScope也一樣)花竞。考慮到user這個變量的生命周期應該要與session相同,所以约急,我使用了SessionStorage零远。

在創(chuàng)建controller時,需要加入$sessionStorage:

app.controller('controller',['$sessionStorage',  function($sessionStorage){}]);  

在登錄成功后厌蔽,將user存儲到SessionStorage中:

$sessionStorage.USER = user;

好了牵辣,之后通過$sessionStorage就可以獲取到用戶信息了。

user = $sessionStorage.USER;

控制頁面路由的跳轉(zhuǎn)

下面我們開始實現(xiàn)第一點:控制頁面路由的跳轉(zhuǎn)奴饮。

要做到第一點比較容易纬向,Angular路由改變時會觸發(fā)$stateChangeStart事件(我用的是stateProvider,所以監(jiān)聽stateChangeStart戴卜,如果是用的route或是location逾条,應該監(jiān)聽它們對應的事件),監(jiān)聽此事件投剥,在里面根據(jù)訪問的url以及用戶角色進行權限判斷师脂,比如登錄的判斷就可以在里面做,訪問那個url需要登錄就直接跳轉(zhuǎn)到登錄界面江锨。

首先先寫一個auth服務吃警,用于權限認證:

/**
 * 基于角色的訪問控制
 */
App.service("auth", ["$http","$sessionStorage", function($http, $sessionStorage){
    var roles = []; // 從后端數(shù)據(jù)庫獲取的角色表
    // 從后端獲取的角色權限Url映射表,結(jié)構(gòu)為{"role":["/page1", "/page2"……]}
    var urlPermissions = {};
    // 去后端獲取
    (function(){
      // 此處為測試方便啄育,直接賦值了酌心,下面也僅以示例為目的,盡量簡單了
      roles = ["admin", "user"]
      urlPermissions = {
        // 管理員可以訪問所用頁面
        "admin":["*"], 
        // 普通用戶可以訪問page路徑下的所有界面(登錄灸撰、注冊等頁面)以及系統(tǒng)主頁
        "user":["page.*", "app.index", "app.detail"] 
      }
    })();
    function convertState(state) {
      return state.replace(".", "\\\.").replace("*", ".*");
    }
    return {
      // 是否有訪問某url的權限
      isAccessUrl:function(url) {
        var user = $sessionStorage.USER;
        for(var role in roles) {
          if(user.role.toLowerCase() == roles[role].toLowerCase()) {
            console.log(urlPermissions[roles[role]])
            for(i in urlPermissions[roles[role]]) {
              var regx = eval("/"+convertState(urlPermissions[roles[role]][i])+"/");
              console.log(regx+ " "+ url)
              if(regx.test(url)) {
                return true;
              }
            }
          }
        }
        return false;
      }
    }
    
}])

roles是角色谒府,從后臺獲取浮毯;urlPermissions是每個角色對應的能被其訪問的url列表,也從后臺獲取泰鸡,可通過后臺配置债蓝。這樣,每次新增角色盛龄,我們就可以動態(tài)為其配置訪問權限饰迹。

最重要的是isAccessUrl方法,傳入url后余舶,isAccessUrl首先會通過$sessionStorage獲取用戶信息啊鸭,取得用戶角色,然后看用戶角色是否在角色表中匿值;若在角色表中赠制,就看此角色是否有訪問url的權限。我們在后臺配置的時候挟憔,是直接指定狀態(tài)钟些,但如果沒有通配符*的話烟号,那么每一個頁面都得寫一個url,所以政恍,就增加了通配符 * 功能汪拥,然后將url列表中的每個url轉(zhuǎn)化為正則表達式,再來驗證篙耗,這樣配置就靈活了很多迫筑。

最后是在run中監(jiān)聽事件$stateChangeStart :

App.run(["$rootScope",'$state', "auth", "$sessionStorage", function($rootScope, $state, auth, $sessionStorage){
  $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
    // 路由訪問控制
    if(toState.name!="page.login" && !auth.isAccessUrl(toState.name)) {  
      // 查看是否需要登錄:
      var user = $sessionStorage.USER;
      if(user == null) {
        event.preventDefault();
        $state.go("page.login");
        return;
      }
      event.preventDefault();
      $state.go("page.error");  
    }
});
}])

好了,現(xiàn)在就實現(xiàn)了url的訪問控制宗弯。

頁面元素的顯示控制

至于第二點铣焊,我的解決方案是自定義指令,下面是示例:

<div zg-access="TEST_ACCESS"></div>

注意罕伯,這里傳入的不是角色曲伊,而是權限。因為用戶角色是可以動態(tài)擴展的追他,如果這里寫的是什么樣的角色才可以訪問這個元素坟募,那以后每新增一個角色都將是一個很大很大的麻煩,因為你得一個個來修改代碼邑狸。下面是自定義指令zg-access的代碼:


/**
 * 元素級別的訪問控制指令
 */

App.directive("zgAccess", function($sessionStorage, $http){
  var roles = []; // 角色
  var elemPermissions = {}; // 角色元素權限映射表懈糯,如{ "role":{"SEARCH"}},role有這個搜索權限
  
  // 后臺獲取
  (function(){
    // 簡便起見单雾,這里直接生成
    roles = ["admin", "user", "visitor"];
    elemPermission = {
      "admin":["*"],
      "user":["SEARCH"],
      "visitor":[]
    }
  })();
  console.log("zg-access");
  return {
    restrict: 'A',
    compile: function(element, attr) {
        // 初始為不可見狀態(tài)none赚哗,還有 禁用disbaled和可用ok,共三種狀態(tài)
        var level = "none";
        console.log(attr)
        if(attr && attr["zgAccessLevel"]) {
          level = attr["zgAccessLevel"];
        }
        switch(level) {
          case "none": element.hide(); break;
          case "disabled": 
            element.attr("disabled", "");
            break;
        }
        // 獲取元素權限
        var access = attr["zgAccess"];
        // 將此權限上傳到后端的數(shù)據(jù)庫
        (function(){
         //upload 
        })();
        return function(scope, element) {
          // 判斷用戶有無權限
          var user = $sessionStorage.USER;
          if(user==null||angular.equals({}, user)) {
            user = {};
            user.role = "visitor";
          }
          var role = user.role.toLowerCase();
          console.log(roles);
          for(var i in roles) {
            var tmp = roles[i].toLowerCase();
            if(role == tmp) {
              tmp = elemPermission[role];
              console.log(tmp)
              for(var j in tmp){
                console.log(tmp[j]+" "+access);
                if(access.toLowerCase() == tmp[j].toLowerCase()) {
                  element.removeAttr("disabled");
                  element.show();
                } 
              }
            }
          }
        };
      }
  }
})

zgAccessLevel是一個屬性硅堆,用來控制級別屿储,如果是none(默認為none),就不顯示元素渐逃;如果是disbaled够掠,就是元素不可用(如Button不可用)。

下面是元素示例:

<button ng-click="" zg-access="SEARCH" zg-access-level="disabled">Search</button>

此時茄菊,若以admin角色或者user角色登錄疯潭,Search按鈕將不可用。

轉(zhuǎn)載請注明原文地址 http://www.zgljl2012.com/2016/08/16/angularjsshi-xian-ji-yu-jiao-se-de-qian-duan-fang-wen-kong-zhi/

Github 項目地址

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末面殖,一起剝皮案震驚了整個濱河市竖哩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脊僚,老刑警劉巖相叁,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡钝荡,警方通過查閱死者的電腦和手機街立,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來埠通,“玉大人赎离,你說我怎么就攤上這事《巳瑁” “怎么了梁剔?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舞蔽。 經(jīng)常有香客問我荣病,道長,這世上最難降的妖魔是什么渗柿? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任个盆,我火速辦了婚禮,結(jié)果婚禮上朵栖,老公的妹妹穿的比我還像新娘颊亮。我一直安慰自己,他們只是感情好陨溅,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布终惑。 她就那樣靜靜地躺著,像睡著了一般门扇。 火紅的嫁衣襯著肌膚如雪雹有。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天臼寄,我揣著相機與錄音霸奕,去河邊找鬼。 笑死脯厨,一個胖子當著我的面吹牛铅祸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播合武,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涡扼!你這毒婦竟也來了稼跳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤吃沪,失蹤者是張志新(化名)和其女友劉穎汤善,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡红淡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年不狮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片在旱。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡摇零,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出桶蝎,到底是詐尸還是另有隱情驻仅,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布登渣,位于F島的核電站噪服,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏胜茧。R本人自食惡果不足惜粘优,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望呻顽。 院中可真熱鬧雹顺,春花似錦、人聲如沸芬位。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昧碉。三九已至英染,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間被饿,已是汗流浹背四康。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狭握,地道東北人闪金。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像论颅,于是被迫代替她去往敵國和親哎垦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理恃疯,服務發(fā)現(xiàn)漏设,斷路器,智...
    卡卡羅2017閱讀 134,714評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法今妄,類相關的語法郑口,內(nèi)部類的語法鸳碧,繼承相關的語法,異常的語法犬性,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    程序員poetry閱讀 114,426評論 24 450
  • 高中時候文理分科瞻离,位置換到年段有名的渣男邊上。 渣男姓吳乒裆,不是和題主說的那種愛嫖的渣套利。 他渣在會同時談好幾個女友。...
    79abdec2c65c閱讀 1,232評論 1 1
  • 1缸兔、新建一個WPF工程 2日裙、工程會自動生成一個App.config配置文件,在原來的文件的基礎上加入數(shù)據(jù)庫的信息 ...
    灬52赫茲灬閱讀 1,927評論 0 0