[ASP.NET]使用透過 AngularJS 來存取 Token Base Web API

本文參考「AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity」來實作
透過 AngularJS 來登入取得 bearer token 及存取 API。

在「Web API bearer token 驗證」 我們建立了以 bearer token 驗證的 Web API。

再來要透過 AngularJS 來登入取得 bearer token 及存取 API。

參考「AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity」來實作,如下,

1.開啟網頁,如果未登入就切到登入畫面

1.1.登入取得 token 後,就將 token 存到 local Storage ,並切到首頁,並可以存取 Values API

image

 

2.開啟網頁,如果已登入就切到首頁,並可以存取 Values API

image

 

而想要一啟動網頁就檢查是否已登入,可以檢查是否可以從 local Storage 取得 token,

並可以存取到一個有權限的 API,如 DummyController ,只設定需要驗證,如下(補在Server端 Web API 裡面),

[Authorize]
public class DummyController : ApiController
{
	public string Get(string id)
	{
		return id;
	}
} 

 

再來就是建立 Client 的程式,如下,

image

 

index.html : 程式主頁面,包含css及js

<html>
<head>
    <meta content="IE=edge, chrome=1" http-equiv="X-UA-Compatible" />
    <title>Token Testing</title>
    <link href="content/css/bootstrap.min.css" rel="stylesheet" />
    <link href="content/css/site.css" rel="stylesheet" />
    <link href="content/css/loading-bar.css" rel="stylesheet" />
    <link href="content/css/font-awesome.min.css" rel="stylesheet" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
</head>
<body ng-app="app">
    <div class="container">
        <div class="row">
            <div class="col-md-2">
                &nbsp;
            </div>
            <div data-ng-controller="indexController" class="col-md-8" data-ng-show="authentication.isAuth">
                Hi, {{authentication.userName}}
                <a data-ng-show="authentication.isAuth" href="" data-ng-click="logOut()">Logout</a></li>
            </div>
            <div class="col-md-2">
                &nbsp;
            </div>
        </div>

        <div class="row">
            <div ng-view="">
            </div>
        </div>
    </div>

    <!-- 3rd party libraries -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.min.js"></script>
    <script src="scripts/angular-local-storage.min.js"></script>
    <script src="scripts/loading-bar.min.js"></script>
    <!-- Load app main script -->
    <script src="app/app.js?v=1"></script>

    <!-- Load services -->
    <script src="app/services/authService.js?v=1"></script>
    <script src="app/services/authInterceptorService.js?v=1"></script>
    <script src="app/services/valuesService.js?v=1"></script>

    <!-- Load controllers -->
    <script src="app/controllers/indexController.js?v=1"></script>
    <script src="app/controllers/homeController.js?v=1"></script>
    <script src="app/controllers/loginController.js?v=1"></script>
</body>
</html>

 

app\app.js : 建立 angular module  (app),並設定常數、route path及app啟動時的身份驗證

(function () {
    'use strict';

    var app = angular.module('app', ['ngRoute', 'LocalStorageModule', 'angular-loading-bar']);

    app.config(function($routeProvider) {
        $routeProvider.when('/home', {
            controller: 'homeController',
            templateUrl: '/app/views/home.html'
        });

        $routeProvider.when('/login', {
            controller: 'loginController',
            templateUrl: '/app/views/login.html'
        });

        $routeProvider.otherwise({ redirectTo: '/home' });
    }); 

     
    var baseUri = 'http://localhost:51528/';
    app.constant('ngAppSettings', {
        baseUri: baseUri,
        clientId:'tokenTestApp'
    });

    //設定$http時,會在header裡加入token
    app.config(function ($httpProvider) {
        $httpProvider.interceptors.push('authInterceptorService');
    });

    app.run(['authService', function (authService) {
        //程式啟動時,檢查是否已有token存在。沒有就導到登入畫面
        authService.validateAuth();
    }]);

})();

 

app\views\home.html : 首頁

<div class="row">
    <div class="col-md-2">
        &nbsp;
    </div>
    <div class="col-md-8">
        <h2>首頁功能區</h2>
        <form name="searchForm" ng-submit="getValue()">
            <input type="number" class="form-control" placeholder="id" data-ng-model="id" required autofocus>
            <button class="btn btn-md btn-info btn-block" type="submit">取Value</button>
        </form>
        <div data-ng-hide="message == ''" class="alert alert-danger">
            {{message}}
        </div>
    </div>
    <div class="col-md-2">
        &nbsp;
    </div>
</div>

 

app\views\login.html : 登入頁

<div class="row">
	<div class="col-md-2">
		&nbsp;
	</div>
	<div class="col-md-8">
		<h2 class="form-login-heading">Login</h2>
		<form role="form"  name="loginForm" ng-submit="login()">
			<input type="text" class="form-control" placeholder="Username" data-ng-model="loginData.userName" required autofocus>
			<input type="password" class="form-control" placeholder="Password" data-ng-model="loginData.password" required>
			<button class="btn btn-md btn-info btn-block" type="submit" ng-disabled="appForm.$invalid">Login</button>
		</form>
		<div data-ng-hide="message == ''" class="alert alert-danger">
			{{message}}
		</div>
	</div>
	<div class="col-md-2">
		&nbsp;
	</div>
</div>

 

app\services\authService.js : 負責登入、登出及驗證token

(function () {
    'use strict';

    angular
        .module('app')
        .factory('authService', authService);

    function authService($http, $q, $location, localStorageService, ngAppSettings) {
        var service = {};

        var _authentication = {
            isAuth: false,
            userName: "",
            useRefreshTokens: false,
            accessToken: ""
        };


        var _login = function (loginData) {
            var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;

            if (loginData.useRefreshTokens) {
                data = data + "&client_id=" + ngAppSettings.clientId;
            }

            var deferred = $q.defer();
            var tokenUrl = ngAppSettings.baseUri + 'token';
            $http.post(tokenUrl, data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
                .success(function (response) {
                    _authentication.isAuth = true;
                    _authentication.userName = loginData.userName;
                    _authentication.accessToken = response.access_token;
                    localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, refreshToken: "", useRefreshTokens: false });
                deferred.resolve(response);
            }).error(function (err, status) {
                console.log(err);
                console.log(status);
                deferred.reject(err);
            });
            return deferred.promise;
        };

        var _logOut = function () {

            localStorageService.remove('authorizationData');

            _authentication.isAuth = false;
            _authentication.userName = "";
            _authentication.useRefreshTokens = false;

        };

        var _validateAuth = function () {
            var dummyUrl = ngAppSettings.baseUri + 'api/dummy/' + Date.now().toString();
            var deferred = $q.defer();
            $http.get(dummyUrl) .success(function() {
                var authData = localStorageService.get('authorizationData');
                if (authData) {
                    _authentication.isAuth = true;
                    _authentication.userName = authData.userName;
                    _authentication.useRefreshTokens = authData.useRefreshTokens;
                }
                console.log(_authentication.isAuth);
            }).error(function (err, status) {
                console.log(err);
                console.log(status);
                deferred.reject(err);
                _logOut();

                $location.path('/login');
            });
        };

        service.login = _login;
        service.logOut = _logOut;
        service.validateAuth = _validateAuth;
        service.authentication = _authentication;
        return service;
    }
})();

 

app\services\authInterceptorService.js : 負責在http的header裡加入 token

(function () {
    'use strict';
    angular
        .module('app')
        .factory('authInterceptorService', authInterceptorService);

    function authInterceptorService($location, $q, $injector, localStorageService) {
        var service = {};
        var _request = function (config) {

            config.headers = config.headers || {};

            var authData = localStorageService.get('authorizationData');
            if (authData) {
                config.headers.Authorization = 'Bearer ' + authData.token;
            }
            return config;
        }

        var _responseError = function (rejection) {
            if (rejection.status === 401) {
                var authService = $injector.get('authService');
                authService.logOut();
                $location.path('/login');
            }
            return $q.reject(rejection);
        }
        service.request = _request;
        service.responseError = _responseError;
        return service;
        
    }
})();

 

app\services\valuesService.js : 負責取得 values API 的資料

(function () {
    'use strict';

    angular
        .module('app')
        .factory('valuesService', valuesService);

    function valuesService($http, $q, ngAppSettings) {
        var service = {};
        var _value = '';
        var _getValue = function (id) {
            var deferred = $q.defer();
            var getValueUrl = ngAppSettings.baseUri + 'api/values/' + id;
             
            $http.get(getValueUrl)
                .success(function (response) {
                    _value = response;
                    console.log(_value);
                    deferred.resolve(response);
                }).error(function (err, status) {
                    console.log(err);
                    console.log(status);
                    deferred.reject(err);
                });
            return deferred.promise;
        };
        service.getValue = _getValue;
        return service;
   
    }
})();

 

app\controller\loginController.js

(function () {
    'use strict';
    angular
        .module('app')
        .controller('loginController', loginController);

    function loginController($scope, $location, authService) {
        $scope.loginData = {
            userName: "",
            password: "",
            useRefreshTokens: false
        };

        $scope.message = "";
        $scope.login = function () {
            authService.login($scope.loginData).then(function (response) {
                $scope.message = authService.authentication.accessToken;
                $location.path('/home');
            },
             function (err) {
                 $scope.message = err.error_description;
             });
        };
         
    }
})();

 

app\controller\homeController.js

(function () {
    'use strict';
    angular
        .module('app')
        .controller('homeController', homeController);

    function homeController($scope, valuesService) {
        $scope.message = '';
        $scope.id = '';
        $scope.getValue = function () {
            valuesService.getValue($scope.id).then(function (response) {
                $scope.message = response;
            },
             function (err) {
                 $scope.message = err.error_description;
             });
            
        };
    }
})();

 

完整詳細的實作,可以參考 AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity

測試Example:AngularJSTokenTestClient

參考資料

AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity

Web API bearer token 驗證

Token Based Authentication using ASP.NET Web API 2, Owin, and Identity

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^