文档章节

基于 Laravel (5.1) & Ember.js (1.13.0) 的用户授权系统

Lee的白板报
 Lee的白板报
发布于 2015/08/16 16:51
字数 1456
阅读 567
收藏 5

Laravel 本身提供了完整的用户授权解决方案,对于由 PHP 驱动的多页面应用,Laravel 能够完美解决用户授权问题。但是在 SPA 中,laravel 退化成一个 API server,页面路由和表单提交完全由前端框架控制,此时面临2个问题:

  1. 如何在前端实现页面访问权限控制?

  2. 如何对 ajax 请求做授权?


如何在前端实现页面访问权限控制?

Ember.js 1.13.0 没有提供 authentication 功能,我使用了一个名为 ember-simple-auth 的第三方扩展。这是它的 Github 主页:

https://github.com/simplabs/ember-simple-auth

首先在你的 ember-cli 项目根目录下安装该扩展:

ember install ember-cli-simple-auth

然后在 ember/config/environment.js 文件中对其进行配置,具体的配置选项在文档中有详细说明,我的配置如下:

// ember/config/environment.js

ENV['simple-auth'] = {
    authorizer: 'authorizer:custom'    //我使用了一个自定义的授权模块
};

Ember-simple-auth 定义了一系列 mixin 类,只要你的 route 继承了某个 mixin, 就获得了它预定义的某些行为或功能。例如,我的 ember/app/routes/application.js 内容如下:

// ember/app/routes/application.js 

import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';

export default Ember.Route.extend(ApplicationRouteMixin, {
    actions: {
        invalidateSession: function() {
            this.get('session').invalidate();
        }
    }
});

application-route-mixin 已经预定义好了一系列 actions 方法。当 session 上的事件被触发时,对应的 action 将被调用来处理该事件。你也可以在 ember/app/routes/application.js 自己的 action 中覆盖这些方法(ember-simple-auth 会在本地 localStorage 中维护一个 session 对象,它保存着前端产生的所有授权信息)。

然后,在只能由已授权用户访问的页面路由中添加 authenticated-route-mixin:

// ember/app/routes/user.js 

import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin,{
    model: function(params) {
        return this.store.find('user',params.user_id);
    }
});

authenticated-route-mixin 保证了只有授权用户才能访问 /user。如果未授权,则默认重定向到 /login 。所以在 ember/app/routes/login.js 中需要添加 unauthenticated-route-mixin :

// ember/app/routes/login.js 

import UnauthenticatedRouteMixin from 'simple-auth/mixins/unauthenticated-route-mixin';

export default Ember.Route.extend(UnauthenticatedRouteMixin);

unauthenticated-route-mixin 保证该路径不需要授权也能访问,这对于 /login 是合理的。


如何对 ajax 请求做授权?

自定义 authenticator : ember/app/authenticators/custom.js

// ember/app/authenticators/custom.js

import Base from 'simple-auth/authenticators/base';

export default Base.extend({

    /**
     * Check auth state of frontend
     *
     * @param data (传入session包含的数据)
     * @returns {ES6Promise.Promise}
     */
    restore: function(data) {
        return new Ember.RSVP.Promise(function(resolve, reject)
        {
            if ( data.is_login ){
                resolve(data);
            }
            else{
                reject();
            }
        });
    },

    /**
     * Permission to login by frontend
     *
     * @param obj credentials
     * @returns {ES6Promise.Promise}
     */
    authenticate: function(credentials) {

        var authUrl = credentials.isLogin ? '/auth/login' : '/auth/register'

        return new Ember.RSVP.Promise(function(resolve, reject) {

            Ember.$.ajax({

                url:  authUrl,
                type: 'POST',
                data: { email: credentials.identification, password: credentials.password }

            }).then(function(response) {

                if(response.login === 'success'){
                    resolve({ is_login : true });
                }

            }, function(xhr, status, error) {

                reject(xhr.responseText);

            });
        });
    },

    /**
     * Permission to logout by frontend
     *
     * @returns {ES6Promise.Promise}
     */
    invalidate: function() {

        return new Ember.RSVP.Promise(function(resolve) {

            Ember.$.ajax({

                url: '/auth/logout',
                type: 'GET'

            }).then(function(response) {

                if(response.logout === 'success'){
                    resolve();
                }
            });
        });
    }
});

restore, authenticate, invalidate 3个函数分别用来获取授权,进行授权,取消授权。

自定义 authorizer : ember/app/authorizers/custom.js

// ember/app/authorizers/custom.js

import Base from 'simple-auth/authorizers/base';

export default Base.extend({

    authorize: function(jqXHR, requestOptions)
    {
        var _this = this;

        Ember.$.ajaxSetup({

            headers:
            {
                'X-XSRF-TOKEN': Ember.$.cookie('XSRF-TOKEN')    // 防止跨域攻击
            },

            complete : function(response, state)
            {
                // 检查服务器的授权状态
                if(response.status===403 && _this.get('session').isAuthenticated)  
                {
                    _this.get('session').invalidate();
                }
            }
        });
    }
});

authorize 函数做了两件事:

  1. 为每一个 ajax 请求添加 'X-XSRF-TOKEN' header

  2. 检查服务器返回的授权状态,并做处理

具体来讲:

header 内容是 laravel 所设置的 'XSRF-TOKEN' cookie 的值,laravel 会尝试从每一个请求中读取 header('X-XSRF-TOKEN'), 并检验 token 的值是否合法,如果检验通过,则认为这是一个安全的请求(该功能在 laravel/app/Http/Middleware/VerifyCsrfToken.php 中实现)。

然后,在 laravel 新建一个中间件(Middleware) ,我把它命名为 VerifyAuth:

<?php

// laravel/app/Http/Middleware/VerifyAuth.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\Auth\Guard;

class VerifyAuth
{
    protected $include = ['api/*'];    // 需要做权限验证的 URL

    protected $auth;

    public function __construct(Guard $auth)
    {
        $this->auth = $auth;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @abort  403
     * @return  mixed
     */
    public function handle($request, Closure $next)
    {
        if( $this->shouldPassThrough($request) || $this->auth->check() )
        {
            return $next($request);
        }

        abort(403, 'Unauthorized action.');     //抛出异常,由前端捕捉并处理
    }


    /**
     * Determine if the request has a URI that should pass through auth verification.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function shouldPassThrough($request)
    {
        foreach ($this->include as $include) {
            if ($request->is($include)) {
                return false;
            }
        }

        return true;
    }
}

它只对 API 请求做权限验证,因为 AUTH 请求是对权限的操作,而除此之外的其他请求都会作为无效请求重新路由给前端,或者抛出错误。如果一个请求是未被授权的,服务器抛出 403 错误提醒前端需要用户登录或者注册。

最后,在 laravel\app\Http\Controllers\Auth\AuthController.php 中实现所有的授权逻辑:

<?php

namespace App\Http\Controllers\Auth;

use App\User;
use Validator;
use Response;
use Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    protected $remember = true;    // 是否长期记住已登录的用户

    public function __construct()
    {
        $this->middleware('guest', ['except' => 'getLogout']);
    }

    public function postLogin(Request $credentials)    // 登录
    {
        return $this->logUserIn($credentials);
    }


    public function getLogout()    // 登出
    {
        Auth::logout();
        return Response::json(['logout'=>'success']);
    }


    public function postRegister(Request $credentials)    // 创建并注册新用户
    {
        $newUser = new User;
    
        $newUser->email = $credentials['email'];
        $newUser->password = bcrypt($credentials['password']);
    
        $newUser->save();
    
        return $this->logUserIn($credentials);
    }
    
    
    protected function logUserIn(Request $credentials)    // 实现用户登录
    {
        $loginData = ['email' => $credentials['email'], 'password' => $credentials['password']];
    
        if ( Auth::attempt($loginData, $this->remember) )
        {
            return Response::json(['login'=>'success']);
        }
        else
        {
            return Response::json(['login'=>'failed']);
        }
    }
}


总结

设置页面访问权限能防止未授权用户访问不属于他的页面,但总归前端是完全暴露给用户的,所以用户的授权状态必须由服务器维护。前端一方面为每个 ajax 请求添加防止跨域攻击的 token, 另一方面当每个请求返回后检查 http status code 是否为 403 权限错误,如果是,则重定向到登录页要求用户取得授权。

© 著作权归作者所有

共有 人打赏支持
Lee的白板报
粉丝 93
博文 31
码字总数 45338
作品 0
高级程序员
私信 提问
laravel (5.1) & Ember.js (1.13.0) 的整合

Lavavel 不必过多介绍了, 作为全世界最流行的PHP框架,有着清晰的架构、完善的文档、丰富的工具等等,能够帮助开发者快速构建多页面web应用程序。 然而,随着技术的发展,web程序的另一面—...

Lee的白板报
2015/08/15
0
1
Ember.js 1.13.0/2.0 Beta 发布

Ember是一个雄心勃勃的Web应用程序,消除了样板,并提供了一个标准的应用程序架构的JavaScript框架。 Ember.js 1.13.0 和 Ember.js 2.0 Beta 发布。Ember.js 1.13.0 至少有 43 名贡献者参与,...

oschina
2015/06/14
1K
5
Flarum 技术架构 Architecture

Flarum 使用三层体系结构。 1、核心层 - core 负责管理数据、事件、实体、命令等。 2、中间层 - API 使用 Web API 来操作 Flarum。 3、最外层 - Web App 使用 Ember.js 展示数据。 这样的分层...

justjavac
2014/12/31
375
0
Laravel 不权威导航

Laravel不权威导航 Hi 这里是Roy整理的Laravel相关索引,希望能帮到大家 ,目前只是一小部分,正在努力整理中... Laravel 文档 Laravel 官方文档 —— 英文 Laravel 中文文档 —— laravel-...

weixingo
2016/02/05
180
0
Laravel 5.1 + OAuth2 PasswordGrant(密码授权模式)

背景简述 本文意在搭建一个通用的应用后端服务环境, 账号验证是应用的基础环境之一. OAuth2可提供安全的验证环境, 以accesstoken作为访问安全资源的令牌, 作为单一的应用端与后端的交互方式,...

黑狗007
2015/10/22
3.8K
9

没有更多内容

加载失败,请刷新页面

加载更多

IC-CAD Methodology企业实战之openlava

在云计算解决安全问题并成为IC界主流运算平台之前,私有的服务器集群系统仍然是各大IC公司的计算资源平台首选。 现在主流的服务器集群管理系统包括lsf,openlava,SkyForm,三者都属于lsf一系...

李艳青1987
15分钟前
0
0
http response stream 字节流 接收与解码

在接收图片、音频、视频的时候,需要用到二进制流。 浏览器会发给客户端 字节Byte流,一串串的发过来_int8格式 -128~127(十进制),也就是8bit(位)。 客户端接收的时候,对接收到的字节收集,...

大灰狼wow
15分钟前
0
0
配置Tomcat监听80端口...

12月13日任务 16.4 配置Tomcat监听80端口 16.5/16.6/16.7 配置Tomcat虚拟主机 16.8 Tomcat日志 1.配置Tomcat监听80端口 示例一:自定义监听端口 vim /usr/local/tomcat/conf/server.xml 编辑...

hhpuppy
15分钟前
0
0
在ubuntu中配置java环境

先在官网下载一个jdk 进入root权限,避免之后出现创建文件失败或者修改文本失败的问题 sudo i 创建一个文件夹来放置jdk解压后的文件 mkdir 文件夹mv jdk1.9(你下载的jdk文件) 你创建 的文件...

无极之岚
16分钟前
0
0
程序中设置MySQL的默认值

import com.alibaba.fastjson.JSON;import java.beans.PropertyDescriptor;import java.lang.annotation.*;import java.lang.reflect.Field;import java.lang.reflect.Method;impo......

laolin23
39分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部