Sunday, August 27, 2017

Angular 1: Tạo App Hoàn Chỉnh P4 - Phân Quyền Truy Cập Cho Từng View(admin role, user role)

phần 3 ta đã tìm hiểu xong về I18n cho ứng dụng của ta, nay ta sẽ tiếp tục với việc phân quyền cho từng user được phép truy cập view nào.

1. Thư viện cần thiết.
Ở phần này chúng ta sẽ cần thêm jquery vì thế ta cần phải tải và import vào trong index.html của ta không quan tâm version của jquery.
Thêm vào bower.json dependencies "jquery": "3.2.1" sau đó run bower install để tải jquery hoặc ta có thể dùng lệnh bower install jquery --save  để tải jquery về.
{
  ...
  "dependencies": {
    ...
    "jquery": "3.2.1"
  }
}
- Tiếp theo ta import vào trang index.html
<body ng-controller="AppCtrl">
  ..
  <script src="bower_components/jquery/dist/jquery.js"></script>
  ...
</body>

2. Cấu hình cho view require login, require permission.
Ta dùng hai thuộc tính requireLoginrequirePermission để đánh dấu cho từng view,
hai thuộc tình này ta có thể thay bằng bất kì tên này mà ta yêu, ở đây thì mình dùng với ý nghĩa, có những trang mình cần phải login, có những trang ko cần và có những trang phải cần vừa có login và một số quyền đặc biệt thì mới truy cập được.
Xem code bên dưới mình có phân một số quyền trong app.js đơn giản để mô tả yêu cầu.
app.config(["$routeProvider", function($routeProvider) {
  $routeProvider
    .when("/admin/san-pham", {
      template: "<div>Chỉ có user với role admin mới truy cập được vào trang này</div>",
      requireLogin: true,
      requirePermission: ["admin"]
    }).when("/", {
      template: "<div>home page <a href='' ng-click='dangXuat()' ng-show='logined'>Đăng xuất</a> <a href='#/dang-nhap' ng-hide='logined'>Đăng nhập</a></div>",
      controller: "HomeCtrl"
    }).when("/san-pham", {
      templateUrl: "app/views/san-pham/san-pham-list.html",
      controller: "ListSanPhamCtrl"
    }).when("/san-pham/:id", {
      templateUrl: "app/views/san-pham/san-pham-detail.html",
      controller: "SanPhamDetailCtrl",
      requireLogin: true
    }).when("/dang-nhap", {
      templateUrl: "app/views/dang-nhap/dang-nhap.html",
      controller: "DangNhapCtrl"
    }).otherwise("/");
}])
Như trên với:
route /admin/san-pham thì user phải login và có quyền admin thì mới truy cập được route này.
route / hoặc /san-pham không cần user phải login.
route /san-pham/:id thì chỉ cần user login là đủ không cần quyền.
3. Cài đặt cho các thuộc tính thực hiện đúng nghĩa của nó.
Tiếp đến để cách thuộc tính requireLogin, requirePermission thực hiện đúng nhiệm vụ của nó ta thêm phần check quyền ở event $routeChangeStart
app.run(["$rootScope", "$translate", "$location", "MyAuth", function($rootScope, $translate, $location, MyAuth) {
    $rootScope.$on('$routeChangeStart', function (event, next) {

      if(!MyAuth.checkPermission(next)) {
        $location.path("/");
      }
      ...
    });
}]);
Ta kiểm tra mỗi khi url bắt đầu thay đổi với hàm check MyAuth.checkPermission(next) nếu không thỏa mản điều kiện tức là user không có login hoặc không có quyền ta ngay lập tức redirect về trang home 
4. Tạo service MyAuth.
Ta tạo file my-auth.js trong thư mục angular1/app/modules với nội dung sau.
(function(){
 "use strict";
 angular.module("Service.MyAuth", [])
  .service("MyAuth", ["MyLocalStorage", function(MyLocalStorage){

   this.checkPermission = function(route) {
    if(!route.requireLogin) {
     // dont require login
     return true;
    } else {
     return this.isLogined() && hasPermission(route.requirePermission);
    }
   }

   this.storeUserInLocalStorage = function(user) {
    // after login successful, we store somewhere in local for check
    MyLocalStorage.setObject("userLogined", user);
   }

   this.clearUserInLocalStorage = function(user) {
    // after login successful, we store somewhere in local for check
    MyLocalStorage.remove("userLogined");
   }

   this.isLogined = function() {
    // get user that logined from localstorage
    return JSON.stringify(MyLocalStorage.getObject("userLogined")) != JSON.stringify({});
   }

   function hasPermission(permssionRequired) {
    var user = MyLocalStorage.getObject("userLogined");
    var permissionOfUser = user.permssion; //["admin","user"]
    return permissionOfUser.indexOf(permssionRequired) != -1;
   }

  }]);
})();
Trong hàm này ta dùng localstorage để lưu trữ user khi user login thành công và cũng xòa dữ liệu khi user logout
- Tiếp đến ta cũng cần phải handle với localstorage vì thế ta tạo file my-localstorage.js trong thư mục angular1/app/modules với nội dung sau.
(function(){
 "use strict";
 angular.module("Service.MyLocalStorage", [])
  .service("MyLocalStorage", ["$window", function($window){
   var localStorage = $window.localStorage;

   this.set = function(key, value) {
                localStorage[key] = value;
            }

            this.get = function(key) {
                return localStorage[key];
            }

            this.setObject = function(key, value) {
                localStorage[key] = angular.toJson(value);
            }

            this.getObject = function(key) {
                var strObject = localStorage[key];
                if (strObject != undefined && strObject !== null)
                    return JSON.parse(strObject);
                return {};
            }

            this.getListObject = function(key) {
                var strObject = localStorage[key];
                if (strObject != undefined && strObject !== null)
                    return JSON.parse(strObject);
                return [];
            }

            this.remove = function(key) {
                localStorage.removeItem(key);
            }

            this.getAllKeys = function() {
                var keys = [];
                for (var i = 0; i < localStorage.length; i++) {
                    keys.push(localStorage.key(i));
                }
                return keys;
            }

            this.removeAllItem = function() {
                for (var i = 0; i < localStorage.length; i++) {
                    this.remove(localStorage.key(i));
                }
            }

  }]);
})();
Chúng ta phải import hai files này trong index.html và đăng ký với ứng dụng trong main.ctrl.js
index.html
<body ng-controller="AppCtrl">
  ...
  <script src="app/modules/my-auth.js"></script>
  <script src="app/modules/my-localstorage.js"></script>
  ...
</body>
main.ctrl.js
angular.module("MyCtrl",[
  ...
  "Service.MyAuth",
  "Service.MyLocalStorage"
  ...
  ])
Ta đã xong phần check quyền cho user, nếu ta cố gắng truy cập vào trang /admin/san-pham thì hệ thống sẽ redirect ta về trang chủ(/).
Giờ ta tạo trang dang-nhap.htmldang-nhap.ctrl.js trong thư mục angular1/view/dang-nhap/ để đăng nhập hệ thống và kiểm tra quyền
dang-nhap.html
<div class="dang-nhap-container">
 <label>Tên đăng nhập:</label><br/>
 <input type="text" ng-model="username"/><br/>
 <label>Mật khẩu:</label><br/>
 <input type="password" ng-model="password"/><br/>
 <button type="button" ng-click="checkLogin()">Đăng nhập</button>
</div>
dang-nhap.ctrl.js
(function(){
 "use strict";
 angular.module("User.DangNhap.Ctrl",[])
  .controller("DangNhapCtrl", ["$scope", "$http", "MyAuth", "$location", function($scope, $http, MyAuth, $location) {
   $scope.checkLogin = function() {
    // call api for checking username/password
    var user = {
     username: $scope.username,
     password: $scope.password
    };
    $http.post("api/login", $.param(user)).
     then(function(response) {
      // check response
      if(response.loginSuccess) {
       // store user in localstore
       // assump that user has role admin
       user.permission = ["admin"];
       MyAuth.storeUserInLocalStorage(user);
       // redirect to /
       $location.path("/");
      }
     }, function(error) {
      // error
      console.log(error);
      console.log("Because we dont have real api for checking username, password");
      console.log("So that, we hard code here!");
      if($scope.username == "admin" && $scope.password == "admin") {
       // store user in localstore
       // assump that user has role admin
       user.permission = ["admin"];
       MyAuth.storeUserInLocalStorage(user);
       // redirect to /
       $location.path("/");
      } else {
       alert("Đăng nhập không thành công");
      }
     });
   }
  }]);
})();
Dừng quên import hai file này vào index.html, và đăng ký với main.ctrl.js và đăng ký route dang-nhap.
index.html
<body ng-controller="AppCtrl">
  ...
  <script src="app/views/dang-nhap/dang-nhap.ctrl.js"></script>
  ...
</body>
main.ctrl.js
angular.module("MyCtrl",[
  ...
  "User.DangNhap.Ctrl",
  ...
  ])
app.js
app.config(["$routeProvider", function($routeProvider) {
    $routeProvider
      ...
      .when("/dang-nhap", {
        templateUrl: "app/views/dang-nhap/dang-nhap.html",
        controller: "DangNhapCtrl"
      })
      ...
  }])
Ở trên file dang-nhp.html ta có một cái form username và password cho user đăng nhập nếu user nhập khác admin/admin thì ta hiển thị thông báo đăng nhập không thành công, ngược lại thì ta sẽ redirect user về trang chủ.
Đăng nhập thành công
Và giờ user có quyền admin ta thử truy cập vào trang admin xem có được không

Trong file dang-nhap.ctrl.js ta nhận dữ liệu từ form và goi api để check login cho user đó, nhưng hiện tại api đó ta ko có sẵn nên ta hard code đăng nhập đó.

Ghi Chú: Trong $http.post của angular ta có dùng $.param sao ta không dùng trực tiếp lun mà phải cần $.param mục đích là gì.
Khi ta dùng $.param dữ liệu của Request Payload có dạng username=admin&password=admin  xem hình bên dưới
Khi ta không dùng $.param dữ liệu cuar Request Payload có dạng {username: "admin", password: "admin"} xem hình bên dưới
Với dữ liệu như trên server của ta rất khó truy xuất tới từng để get giá trị của từng field và nó thích hợp với việc gới 1 json lên server rồi từ đó ta parse json ra object ta muốn get.

Tải source code
https://drive.google.com/open?id=0B-JvPy4-xKqkalJUYTA0dk9hLXM


No comments:

Post a Comment