将身份验证信息从MVC传递到角度

时间:2016-04-28 12:14:13

标签: angularjs asp.net-mvc

我的项目使用MVC来提供我网站的初始标记

MVC控制器非常简单:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

我在布局视图中有我的ng-app标签,捆绑和@RenderBody:

<!DOCTYPE html>
<html ng-app="myAppName">
<head>
    @Styles.Render("~/Content/css")
</head>
<body>
    <div class="container body-content">
        @RenderBody()
    </div>

    @Scripts.Render("~/bundles/aBundle")
</body>
</html>

我的索引视图尽可能简单:

<ng-view></ng-view>

我的angular app.ts文件如下所示:

module app {
    var main = angular.module("myAppName", ["ngRoute", "breeze.angular"]);

    main.config(routeConfig);

    routeConfig.$inject = ["$routeProvider"];
    function routeConfig($routeProvider: ng.route.IRouteProvider): void {
        $routeProvider
            .when("/home",
            {
                templateUrl: "app/views/homeView.html",
                controller: "HomeController as vm"
            })
            .when("/itemDetail/:itemId",
            {
                templateUrl: "app/views/itemDetailView.html",
                controller: "ItemDetailController as vm"
            })
            .when("/addItem",
            {
                templateUrl: "app/views/addItemView.html",
                controller: "AddItemController as vm"
            })
            .when("/login",
            {
                templateUrl: "app/views/loginView.html",
                controller: "LoginController as vm"
            })
            .otherwise("/home");
    }
}

我可以检查用户在MVC控制器中发送的请求,或者在Razor视图中使用@Request.IsAuthenticated查看用户是否已登录,但是将此信息传递给我的角度应用程序的最佳方法是什么这样我可以在用户首次登录时正确地将用户路由到登录页面,但如果他们在服务器上有活动会话,则跳过登录页面?

我尝试解决这个问题的研究告诉我,我可能需要创建一个角度服务来存储关于用户是否经过身份验证的布尔值。然后,我需要添加一些代码来使用$ routeChangeStart检查每个路由的此服务,并仅在必要时重定向到登录页面。我已经看了很多例子,但是在我自己的应用程序的上下文中不能完全把它们放在一起。

有人可以帮我点点滴滴吗?

3 个答案:

答案 0 :(得分:0)

我正在开发一个我不使用剃须刀的项目。视图中只有html和angular。所以...我做的是:

我创建了一个包含布局的指令“appWrapper”。在它内部有一个具有ng-view的部分。 该指令使用控制器AuthCtrl,此控制器使用服务AuthService。 因此,我可以访问整个html中此控制器内的所有内容。例如,我可以在侧边栏的项目中说ng-click =“vm.logout()”。 这个“AuthService”有一些可以被控制器调用的方法。登录并注销其中2个。 当我执行登录时,我设置了一些cookie,其中包含我从控制器返回的一些信息。 当我退出时,我删除了这些cookie。 在我的路由所在的app.js上,我最后在运行结束时检查这些cookie,每次更改位置(url)时都会检查这些cookie。如果存在,则允许用户继续。如果没有,它会将用户重定向到登录名。

有帮助吗?如果需要,我可以发布代码供您使用。

无需在控制器内创建操作以进行检查。它需要您的应用程序转到服务器,返回浏览器,返回服务器并返回浏览器以执行用户执行的每个操作。这不好。

答案 1 :(得分:0)

我想你会按照以下方式做到这一点:

MVC控制器:

[Authorize] // Make sure we're authorising the whole controller.
public class ProfileController : Controller
{
    public ActionResult IsLoggedIn()
    {
        return Json(this.Request.IsAuthenticated); 
        // you may need allow get as the second parameter
    }
}

角:

app.factory('ProfileService', function ($http) {
    var service = {
        isLoggedIn: isLoggedIn
    };

    return service;

    function isLoggedIn() {
        return $http.get('Profile/IsLoggedIn').then(function (response) {
            return response.data; // this depends on what the response object looks like
        });
    }
});

app.run(function (ProfileService, $location) {
    ProfileService.isLoggedIn().then(function (isLoggedIn) {
        if (!isLoggedIn) {
            $location.path('/unauthorised'); // just redirect them to the unauthorised route.
        }
    });
});

这意味着每次您的应用运行时,它都会检查您是否已登录。您还可以使用此配置文件服务获取有关该用户的其他信息!您可以将此服务提取到您希望执行此类工作的任何其他模块中。

请记住,尝试保护javascript是没有意义的,因为它是在沙箱中运行的。但始终要确保在MVC代码中使用[Authorize]属性,以便服务器始终强制执行授权和身份验证。

在有人说这不是Typescript之前,任何Javascript也都是有效的Typescript。我留给用户输入defs类型。

- 更多信息 -

如果您不断要求提供信息,可以在登录时添加x分钟缓存到期时间或将这些详细信息存储到本地存储中:

app.factory('ProfileService', function ($http, $q) {
    var service = {
        isLoggedIn: isLoggedIn
    };

    var cacheExpiries = {
        loggedIn: { value: null, expiry: null }
    };

    return service;

    function isLoggedIn() {
        var cacheObj = cacheExpiries['loggedIn'];
        var useCache = false;
        if (cacheObj.expiry) {
            useCache = new Date() < cacheObj.expiry;
        } 

        if (useCache) { 
            // because http returns a promise we need to 
            // short circuit function with a promise
            return $q(function (res, rej) {
                res(cacheObj.value); 
            });
        }

        // set the new expiry for the cache, this just adds 5 minutes to now
        cacheObj.expiry = new Date(new Date().setMinutes(new Date().getMinutes() + 5));

        return $http.get('Profile/IsLoggedIn').then(function (response) {
            cacheObj.value = response.data;
            return response.data; // this depends on what the response object looks like
        });
    }
});

您可以使用正确的key value存储API轻松将缓存封装到工厂中。这将从瘦服务中提取一些逻辑(以保持稀薄)

本地存储和Cookie存储

我已经使用这个module多年来进行本地存储访问,这可以用来代替缓存,你仍然可以使用缓存服务来包装这个存储解决方案你可以完全与你的依赖关系分离。

答案 2 :(得分:0)

我可以分享我在自己的应用中所做的事情,虽然我使用的是ui-router,但技术方法相同[{3}}。

基本逻辑工作流程:

  1. 自动为获取用户当前身份验证状态的每条路线添加resolve并对其进行缓存。
  2. 检查路由是否需要身份验证,如果用户未登录则拒绝,否则允许正常继续。
  3. $routeChangeError(或类似的东西)上,检查要执行的操作。
  4. 这样做的好处是它可以让您在提供良好的客户端安全体验方面具有很大的灵活性。

    为每条路线添加凭据:

    let originalStateFunction = $stateProvider.state;
    
    $stateProvider.state = function (state, config) {
    
      //
      // The "allowAnonymous" is something we added manually
      // This will become important later because we might have
      // routes that don't require authentication, like say
      // the login page
      //
      if (angular.isDefined(config) && !config.allowAnonymous) {
          _.defaults(config.resolve || (config.resolve = {}), {
              /*@ngInject*/
              userSession: function userSessionSecurityCheck($q, sessionService) {
                  let def = $q.defer();        
    
                  sessionService.getSession()
                    .then(session => {
                       if(!session.isAuthenticated){
                          def.reject({
                            error:'AUTHENTICATION_REQUIRED'
                          });
                       }
                    });
    
                  return def.promise;
    
                  //You could also do more complex handling here...
                  // like check permissions for specific routes
                  // and reject the promise if they fail.
              }
          });
      }    
    
      //Now call the original state/when method with our
      // newly augmented config object
      return originalStateFunction.apply(this, arguments);
    };
    

    检查路线错误

    .run($rootScope => {
        "ngInject";
    
        $rootScope.$on('$stateChangeError', (event, toState, toParams, fromState, fromParams, error) => {
             event.preventDefault();
             if(error.error === "AUTHENTICATION_REQUIRED"){
                  //Now you can redirect the user appropriately
             }
        });
    });
    

    您可以通过检查Principal来创建一个简单的端点来返回当前用户的状态,只要您在客户端缓存该端点,就只会在每个用户中产生一次命中。

    完整的示例代码对于SO来说太大了,但希望这为您提供了一个很好的起点。