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

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 权限错误,如果是,则重定向到登录页要求用户取得授权。

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

时间: 2024-08-09 22:02:04

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

laravel (5.1) & Ember.js (1.13.0) 的整合

Lavavel 不必过多介绍了, 作为全世界最流行的PHP框架,有着清晰的架构.完善的文档.丰富的工具等等,能够帮助开发者快速构建多页面web应用程序. 然而,随着技术的发展,web程序的另一面--客户端,正在变得越来越多元(PC,手机,平板,其他专用设备等).所以需要一种统一的机制,方便服务器与不同的设备进行通信.Restful API 就是基于这个思想被提出来的. 阮一峰给出了对Restful架构的总结: 每一个URI代表一种资源: 客户端和服务器之间,传递这种资源的某种表现层: 客户端通过

ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1

什么是 ThinkSNS+09 年,由北京的团队开发了 ThinkSNS 涉足社交开源行业.这么多年累计不少客户.2014-2016,两年都在维护和开发之前基于 TP 的 ThinkSNS , 慢慢的引入新开发概念.终于,在2016年下半年,我们决定重写这个程序,抛弃之前的每一行代码.框架上,开发人员一致性的选择了 Laravel ,并取了一个看起来像手机厂商给手机命名的名字----ThinkSNS Plus 没错就是 Plus 也就是符号 + 因为我们更希望侧重移动端,这就是 ThinkSNS

基于 Laravel 开发 ThinkSNS+ 中前端的抉择(webpack/Vue)踩坑日记

在上一篇文章< ThinkSNS+基于Laravel master分支,从1到 0,再到0.1>,简单的介绍了 ThinkSNS+ ,这里分享在开发过程中,前端选择的心理活动. Laravel Mix的放弃 在 Laravel 中,前端工作流默认是由 laravel-mix 包驱动的,集成了 Vue.js.而作为核心开发之一,也负责前端这块的开发.其实,这是seven第一次写 Vue,之前都是用 React 做开发. 然后seven和另一个核心成员 Wayne 在楼道抽烟聊前端这事情,要不要用

【社交系统ThinkSNS+研发日记三】基于 Laravel Route 的 ThinkSNS+ Component

[社交系统ThinkSNS+研发日记系列] 一.<ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1> 二.<基于 Laravel 开发 ThinkSNS+ 中前端的抉择(webpack/Vue)踩坑日记> 在前面,我介绍了拓展类型,分别有 plus-compnent 和 plus-plugin 两个,这里重点讲以下如何实现 plus-component 的. plus-component 是什么 就如同名字一样,plus 代表的是 Thin

Ember.js 入门指南——路由简介

从本文开始,将为大家介绍路由(route),如果你看过前面的<Ember.js 入门指南--{{link-to}} 助手>这篇文章应该初步了解了route.不过在这篇文章中只是简单介绍了路由是定义.路由层次,更深入的route将从本文开始逐一介绍. 当用户使用你的应用时,应用要在不同的状态之间切换.Ember提供了很多工具用于管理那些因应用规模改变而改变的状态. 讲route前先了解URL,在应用中大概会会有如下方式设置URL: 用户第一次加载应用的时: 用户手动改变URL,比如点击按钮之后跳

ember.js的环境(window)安装及创建应用

现如今.我们经常都可以看到复杂的JavaScript应用程序,由于这些应用程序变得越来越复杂,一长串的jQuery回调语句或者通过应用程序在各个状态执行不同的函数调用,这些做法都会变得无法再让人接受,这导致了JavaScript开发人员开始寻找一种组织和效率更优秀的开发方式.实现组织和效率的其中一个最常用的架构模式,就是我们熟知的Model View Controller (MVC)模式,这种模式鼓励开发人员将其应用程序的不同部分分割为更易于管理的模块,我们不必使用一个函数直接调用数据库,通过创

点燃圣火! Ember.js 的初学者指南

转自:http://www.adobe.com/cn/devnet/html5/articles/flame-on-a-beginners-guide-to-emberjs.html 作者 Andy Matthews 现在,到处都可以看到复杂的 JavaScript 应用程序. 由于这些应用程序变得越来越复杂,一长串的 jQuery 回调语句,或者通过应用程序在各个点执行不同的函数调用,这些都变得无法再让人接受. 这导致了 JavaScript 开发人员了解到传统的软件程序员已经知道了几十年的问

PHP 基于laravel框架获取微博数据之一 模拟新浪微博登录

参考资料:http://www.csuldw.com/2016/11/10/2016-11-10-simulate-sina-login/http://blog.csdn.net/fly_leopard/article/details/51148904http://www.tuicool.com/articles/uIJzYff http://blog.csdn.net/u010029983/article/details/46364113等 模拟新浪微博登录是抓取新浪数据的基础,网上的参考资料

Ember.js 目前非常流行的H5框架

Ember.js是一个MVC的JavaScript框架,由Apple前雇员创建的SproutCore 2.0改名进化而来,号称「A framework for creating ambitious web applications」. 简介 Emberjs--一个用于创建 web 应用的 JavaScript MVC 框架,采用基于字符串的Handlebars模板,支持双向绑定.观察者模式.计算属性(依赖其他属性动态变化).自动更新模板.路由控制.状态机等. Ember使用自身扩展的类来创建Em