angular auth - meetbill/butterfly-fe GitHub Wiki

AngularJWT 认证

1 认证

1.1 Angular-jwt 部署

angular-jwt

1.1.1 加载 js 及注入模块

(1) 加载 js 文件,默认已加载

文件:templates/index.html

<!-- jwt -->
<script src="static/vendor/angular/angularJwt/services/authManager.js"></script>
<script src="static/vendor/angular/angularJwt/services/interceptor.js"></script>
<script src="static/vendor/angular/angularJwt/services/jwt.js"></script>
<script src="static/vendor/angular/angularJwt/services/jwtOptions.js"></script>
<script src="static/vendor/angular/angularJwt/services/urlUtils.js"></script>
<script src="static/vendor/angular/angularJwt/angularJwt.js"></script>

(2) 依赖注入,默认已注入

文件:static/js/app.js

angular.module('app', [
    '...',
    'angular-jwt'
]);

1.1.2 使用(默认未打开)

使用此模块,需要知道如何在本地获取 token,登录失败时如何操作,获取 token 中的信息

文件:config.js---- 拦截器及相关配置

.config(['$httpProvider','jwtOptionsProvider',function($httpProvider, jwtOptionsProvider) {
    // Please note we're annotating the function so that the $injector works when the file is minified
    jwtOptionsProvider.config({
        // 配置如何在本地获取 token
        //------------------------------------------------------ 从本地 $localStorage 获取
        tokenGetter: ['$localStorage',function($localStorage) {
            return $localStorage.jwt;
        }],
        //------------------------------------------------------ 从 cookie 中获取
        tokenGetter: ['$cookies',function($cookies) {
            return  $cookies.get("butterfly_token");
        }],
        // 认证失败时,则进行跳转到 "access.signin"
        unauthenticatedRedirector: ['$state', function($state) {
            $state.go('access.signin');
        }],
        // 发送请求时携带的 token 前缀
        authPrefix: 'Bearer: '
    });

    $httpProvider.interceptors.push('jwtInterceptor');
}])

文件:config.router.js 订阅特定事件

angular.module('app')
  .run(
    [          '$rootScope', '$state', '$stateParams', 'authManager',
      function ($rootScope,   $state,   $stateParams,  authManager) {
          $rootScope.$state = $state;
          $rootScope.$stateParams = $stateParams;

          // 如下为新增内容
          // 切换路由时等都会进行校验 token
          authManager.checkAuthOnRefresh();
          // 认证失败时,进行跳转到普通登录页面
          authManager.redirectWhenUnauthenticated()
          // 认证失败时,进行跳转到单点登录特定页面(需要跳转的页面由 401 页面中返回)
          authManager.redirectWhenUnauthenticatedSSO()
      }
    ]
  )

获取 username

在 static/vendor/angular/angularJwt/services/authManager.js 中设置了当路由刷新时进行重新校验 token,同时进行获取用户名

decode_token = jwtHelper.decodeToken(token);
$rootScope.username = decode_token.username;

在 UI 中可以直接通过 __username__ 获取用户名
<span class="hidden-sm hidden-md">__username__</span>

1.2 处理认证失败事件

跳转到 butterfly-fe 站内登录页面

监听到 "unauthenticated" 事件,进行跳转到站内登录路由

文件:config.router.js

authManager.redirectWhenUnauthenticated()

登录到 butterfly-auth /auth/ssologin

如果认证使用的 "单点登录 SSO", 则监听到 "unauthenticated" 事件,进行跳转到站外登录接口

文件:config.router.js

authManager.redirectWhenUnauthenticatedSSO()

2 源码解读

2.1 authManager

使用 authManager 管理身份验证状态的

authmanager

{
    authenticate: authenticate,                                     // 设置 $rootScope.isAuthenticated = true;
    unauthenticate: unauthenticate,                                 // 设置 $rootScope.isAuthenticated = false;
    getToken: function(){ return invokeToken(config.tokenGetter); },// 获取 token
    redirect: function() { return invokeRedirector(config.unauthenticatedRedirector); }, // 重定向位置
    checkAuthOnRefresh: checkAuthOnRefresh,                         // 在页面刷新时获取身份验证状态
    redirectWhenUnauthenticated: redirectWhenUnauthenticated,       // 如果有接口返回 401 ,重定向到登录页面
    isAuthenticated: isAuthenticated,                               // 获取 $rootScope.isAuthenticated 状态
    redirectWhenUnauthenticatedSSO: redirectWhenUnauthenticatedSSO, // 单点登录跳转
}

2.2 发布、订阅

发布 ----- 注册拦截器 angularJwt/services/interceptor.js

angular.module('angular-jwt.interceptor', [])
.provider('jwtInterceptor', function() {
    return {
        request: function(config) {
            // do something on request success
            // 此处将所有请求添加了 jwt header
        },
        requestError: function(rejection) {
            // do something on request error
        },
        response: function(response) {
            // do something on response success
        },
        responseError: function(rejection) {
            // do something on response error , 这的 $broadcast 就是进行发布事件
            // 例子:$scope.$broadcast('EVENT_NAME', 'Data to send');
            if (response !== undefined && response.status === 401) {
                $rootScope.$broadcast('unauthenticated', response);
              }
              return $q.reject(response);
        }
    };

订阅之开启:config.router.js

angular.module('app')
  .run(
    [          '$rootScope', '$state', '$stateParams', 'authManager',
      function ($rootScope,   $state,   $stateParams,  authManager) {
          $rootScope.$state = $state;
          $rootScope.$stateParams = $stateParams;

          // 如下为新增内容
          // 检查是否 jwt token 是否已过期
          authManager.checkAuthOnRefresh();
          // 开启订阅 'unauthenticated' 事件,然后决定是否进行跳转等操作
          authManager.redirectWhenUnauthenticated()
          // 开启订阅 'unauthenticated' 事件,进行单点登录操作,跳转到站外登录接口
          authManager.redirectWhenUnauthenticatedSSO()
      }
    ]
  )

订阅之事件处理

跳转站内登录接口

function redirectWhenUnauthenticated() {
    $rootScope.$on('unauthenticated', function () {
        invokeRedirector(config.unauthenticatedRedirector);
        unauthenticate();
    });
}

跳转站外登录接口

function redirectWhenUnauthenticatedSSO() {
        $rootScope.$on('unauthenticated', function (event,response) {
          unauthenticate();
          target_url = response.data.data.redirect;
          $window.location.href = target_url;
        });
      }

2.3 HTTP jwtInterceptor 拦截器

angular/angularJwt/services/interceptor.js

拦截器检查 responseError 时,则发布 'unauthenticated' 事件

3 鉴权

权限的设计中比较常见的就是 RBAC 基于角色的访问控制,基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。

在 Angular 构建的单页面应用中,要实现这样的架构我们需要额外多做一些事。从整体项目上来讲,大约有 3 处地方,前端工程师需要进行处理。

(1) UI 处理(根据用户拥有的权限,判断页面上的一些内容是否显示)
(2) 路由处理(当用户访问一个它没有权限访问的 url 时,跳转到一个错误提示的页面)
(3) HTTP 请求处理(当我们发送一个数据请求,如果返回的 status 是 401 或者 401, 则通常重定向到一个错误提示的页面)

对应 Angulr

UI 处理 ------------> static/tpl/blocks/nav.html 根据角色展示不同的页面
HTTP 请求处理 ------> angular-jwt 认证部分

3.1 个人想的处理方式

UI 处理

角色与导航关系存储在前端
用户认证成功后,后端返回给前端此用户的角色
前端根据此用户的角色更新 static/tpl/blocks/nav.html

4 他山之石

https://github.com/userapp-io/userapp-angular

https://github.com/auth0-blog/angularjs-jwt-authentication-tutorial

这个 angularjs-jwt-authentication-tutorial 使用的是旧版本的 angular-jwt ,Provider 和 变量等都改过名字,所以仅供参考下

https://github.com/thuslako/Portfolio-/blob/92d017a035363b690120d6a2ebbf45ff6a16116f/public/libs/angular-jwt/README.md

5 传送门

⚠️ **GitHub.com Fallback** ⚠️