PHP - 信用卡卡号算法函数

可以使用以下PHP函数
验证一个卡号是否是信用卡

function

validateCard
(
$cardnumber
)


{

    

$cardnumber
=
preg_replace
(
"
/\D|\s/
"
,
""
,
$cardnumber
)

# strip any non-digits

    

$cardlength
=
strlen
(
$cardnumber
)
;
    

if

(
$cardlength
!=
0
)

    

{

        

$parity
=
$cardlength
%
2
;
        

$sum
=
0
;
        

for

(
$i
=
0
;
$i
<
$cardlength
;
$i
++
)

        

{

            

$digit
=
$cardnumber
[
$i
]
;
            

if

(
$i
%
2
==
$parity
)

$digit
=
$digit
*
2
;
                

if

(
$digit
>
9
)

$digit
=
$digit
-
9
;
                    

$sum
=
$sum
+
$digit
;
        

}

        

$valid
=
(
$sum
%
10
==
0
)
;
        

return

$valid
;
    

}

    

return

false
;

}

做外贸网店搞电子商务最常见的网上支付风险问题几点总结

网上支付风险
一直伴随着外贸零售
,骗子到处都有。国际在线支付
难免存在支付风险,只能防范网上支付风险
,减少损失。开外贸网店
一般只有信用卡及paypal存在支付风险。网上支付风险主要有以下几点:

1)收货地址与账单地址不一致,这是最常见的支付风险。无论是信用卡还是paypal,都要求两个地址一致,否则不承认发货。可以和买家沟通,让收货地址与账单地址一致,paypal支持账户多地址,或者让买家在网上把收货地址也添加到账户,如果是老客户,地址不用太较真。在zen cart后台订单页面,地址不匹配会有红点提醒。

2)一单多货。小心了,这可能是骗单!客人支付后收到货或甚至还没收到就拒付。只要发觉客户购买的数量超过常规,就要注意风险。碰到这种情况应该先跟客人沟通,退款让他们用西联再汇款,我们给他一些折扣。如果不行,有时候还得放弃。

3)paypal通过支票支付。支票支付一般要三四天到账,一定要等款到了再发货。有些客人通过支票支付生成订单等确认发货后取消支付。这期间要耐心跟客人解释,因为客人的paypal账户显示已支付,通常的客人都没什么问题。

4)乱投诉,这点最头疼。努力提供证据证明自己吧,实在不行退款认载。

出现这几种情况就要注意网上支付风险,尽量减少损失。

学外贸零售,请继续关注外贸博客

外贸网站的几点经验总结

接触外贸电子商务也半年多了,现在总结了几点。主要从外贸货源,网店建设,网店推广,网上收款,英语交流,国际物流,其他等等方面介绍点小经验,和大家分享分享。

一、外贸货源
1、外贸货源上哪里找,怎么找?每个人都有自己的渠道。只要你用心,一定可以找到更好的。做外贸最出名的有广州的手表和包包;莆田的鞋;深圳的电子。
2、如果还不会找,就上百度搜索一下。包包代销批发,鞋代销批发,手饰代销批发等等,非常多,而且是免费提供图片给你。不用进货,一件代发货。这样你不用 担心没钱进货,货卖不出了吧,实现零库存。只要把卖家提供给你的图片传上网店就可以行了。货物的质量,当然你也得仔细跟踪研究一下。
3、只要拿到质量好的,批发价格便宜的,之后卖得贵的产品就是好货源,利润越大越好。不要看小货源哦,可能这就是你成功的一半。

二、外贸网店的建设
1、为什么要建网店呢?上商务平台(如ebay)上卖不就行了吗?但是你考虑过ebay的卖家有多少了吗?平台的市场价格是非常明显的。有些价格怕是比你进货的价格还便宜。竞争何其激烈。所以一定要建独立的外贸网店。
2、拥有独立外贸网店的就可以有自己的地盘,不用和人家在平台上死掐了。独立外贸网店还可以树立自己的品牌和信用度,和实力等等。
3、一个好的外贸网店一定要在在图片拍摄、产品价格、客户服务、客户体验还有色彩调配等方面着手建设。
4、选择合适的域名,域名要好记简单,可以表现出我们的产品特色等,让人家一看就知道你的网站是做什么的。好的域名还利于搜索引擎。
5、选择好的空间来放置网站,我们做外贸生意,客户都是外国人,当然我们的网站空间选择外国的空间好,这样外国客户访问我们的网站速度就会快,客户体验就会好,生意自然不差了。

三、外贸网店的推广
1、外贸网站建设好后,不做宣传,不告诉人家你的网点。客户是不会自动找上门的,宣传是关键。
2、首先要做得就是网站的搜索引擎优化
了(SEO
),搜索引擎有付费和免费的,经济允许的话开始的时候结合来做,之后靠免费的自然排名就可以了,这样效果比较好,最好是请专业人士来做。
3、接着你可以到国外一些相关的论坛去发贴推广。如果是针对美国客户,你可以去美国一些社区发贴推广。这是最直接、最有效的方法。但发贴的内容需要是软文(软性广告),还有就是要充分利用论坛的个性签名,把自己经营的产品和网址放上去。
4、充分利用免费资源,免费社区、论坛、博客、MSN等等做宣传。

四、外贸网上收款
1、外贸网上收款,PayPal
做外贸得一般都有的,但是paypal的缺点就是一旦发现有违反被寓为圣经一样的规则的时候立刻会被冻结帐号。账号被封,客户流失。
2、那网上用什么收款的,当然是用信用卡收款通道,也就在你网站上安装一下信用卡收款通道,大概就跟个支付宝一样,但不同的是支付宝是收人民币的,而信用 卡收款通道是可以收美金的,这个收汇通道你只要安装好后,老外有张信用卡就能直接在你网店上支付。简单方便。何况信用卡收款通道是网关模式和paypal 的账号模式是没有冲突的,可以同时使用。
3、这个信用卡收款通道,想具体了解、详细介绍可以Q我1274899661

五、外贸英语问题
1、老外买东西来来去去无非就是那几个常用的单词句子。比如做服装,常用的英语有:号码、质料、质量、服装品牌、还有收款方式、邮寄方式等。
2、老外一般不会给你打电话,电话费都够买衣服了。所以口语一般就可以了。
3、一般老外都是和你用MSN或者Email联系的,而你只要打开个google在线翻译,或者下个翻译软件就可以。一边聊一边翻译。
4、边做外贸也得边学习英文,不断提高自己,这样才可以把让外贸做起来更好。

六、国际物流方面
1、外贸当然是走国际物流了,国际上主要的物流有下面几个,具体要看经验了,慢慢摸索。
2、UPS(united parcel serice) 联合包裹服务,美国公司,世界最大的快递公司。主要是按公布价打折的,在按重量的基础上按公布价打折。
3、FEDEX(Federal Express)联邦快递,美国公司。主要是按公布价打折的,和UPS差不多一样。
4、TNT 敦豪快递,欧洲荷兰邮政,欧洲最大的快递公司,在欧洲市场占有率65%,按首重与续重计费的。
5、DHL 欧洲公司 德国公司,在欧洲仅次于TNT。按首重与续重计费的。
6、EMS 中国电信 在中国占有很大的比例。
6、例如中国出发,FEDEX和UPS的强项在美洲线路,日本线路,TNT在欧洲和西亚、中东有绝对优势,DHL则是去日本、东南亚、澳洲有优势。

七、其他方面
1、防止被欺骗,和客户要充分交流和选择好的支付收款通道都对预防欺骗有很大的作用。
2、及时检查店铺产品,是否有错漏,并及时更正。多去论坛做宣传。
3、生意不成做朋友,如何让你的客人成为你的朋友,也成为你的回头客,注意口碑营销很有效的。
4、客户维护,对于经常购买的客户要保持联系,节假日发个卡片或者电子贺卡。
5、和客户交流显得专业、热情、细心的。让买家都能用心体会出来,这基本上会决定一件商品能否成交,提高成交率。这一点非常有效。

fleaPHP里的RBAC权限模型

 

 

转自:http://old.fleaphp.org/index.php?q=book/export/html/36


RBAC 是英文(Role-Based Access Control)的缩写,也就是基于角色的访问控制。RBAC 的定义比较晦涩,我就以比较生动的形式来阐述什么是 RBAC。





ATM 机的一天

假设有一台 ATM(自动提款机)放在街边,我们来看看这个 ATM 度过的一天。


  1. 早上,有一个家伙走到 ATM 面前,对着机器说:“芝麻开门,芝麻开门,给我 100 块!”。很显然 ATM不会有任何动作。失望之余,这个家伙踢了 ATM 一脚走了。
  2. 中午,一位漂亮的 Office lady 走到 ATM 机面前,放入她的信用卡,输入密码后,取出了 1200 块钱。当然,这些钱很快就会变成一件衣服或是化妆品。
  3. 下班时分,银行的工作人员来到 ATM 机器面前,放入一张特制的磁卡,然后输入密码。从中查询到 ATM 机器内还有充足的现金,无需补充。所以他很高兴的开着车去下一台 ATM 机器所在地了。

现在我们要开发一台具有同样功能的 ATM 机,应该怎么做呢?

首先,我们的 ATM 机不能让人随便取钱,不然银行会破产的。接下来,ATM 机需要一个让人们放入磁卡并输入密码的设备。人们放入磁卡并输入密码后,ATM 机还要能够判断这张磁卡的卡号和密码是否有效,并且匹配。之后,ATM 机必须判断磁卡的卡号属于哪种型,如果是信用卡,那么则显示查询账户余额和取款的界面。如果是特制的磁卡,则显示 ATM 机内的现金余额。





ATM 与 RBAC

上面的例子显得有点荒诞,但是却是一个典型的基于角色的访问控制。


  1. 对于没有磁卡或者输入了错误密码用户,一律拒绝服务,也就是不允许进行任何其他操作;
  2. 如果输入了正确的密码,必须判断用户输入哪一种型,并提供相应的服务界面;
  3. 如果用户尝试访问自己不能使用的服务,那么要明确告诉用户这是不可能的。

这个流程中,一共出现了两种角色:信用卡用户和管理卡用户。而那些没有磁卡的用户,都属于没有角色一。RBAC 要能够工作,至少需要两个数据:角色信息访问控制表

角色信息通常是指某个用户具有的角色,例如你持有一张信用卡,那么你就具有“信用卡用户”这个角色。如果你持有一张管理卡,那么你就具有“管理卡用户”这个角色。如果你既没有信用卡,又没有管理卡,那么你就没有上述两种角色。

有了角色信息,RBAC 系统还需要一个访问控制表访问控制表(Access Control Table)是一组数据,用于指出哪些角色可以使用哪个功能,哪些角色不能使用哪个功能。例如在 ATM 机中,具有“信用卡用户”角色,就可以使用查询账户余额和取款两项功能;而具有“管理卡用户”角色,就可以使用查询 ATM 机内现金余额的动能。

我们来模拟一次 ATM 机的操作:


  1. 唐雷有一张信用卡,他放入 ATM 机并输入了正确的密码。这时,他被 ATM 机认为具有“信用卡用户”角色。
  2. 根据上面的判断结果,ATM 机显示了一个操作界面,上面有查询账户余额和取款两项操作按钮
  3. 唐雷按下了“查询账户余额”按钮,ATM 机的查询账户余额功能调用
  4. 在查询账户余额功能中,再次检查用户的角色信息,确定他可以使用这个功能
  5. 进行系列操作,然后将唐雷信用卡账户上的余额数字显示到屏幕上。
  6. 唐雷很郁闷他的信用卡又透支了,悻悻然取出卡走人了。这时 ATM 自动清除当前的角色信息,为下一次操作做好准备。

从上面可以看出,RBAC 充当了系统的一道安全屏障。所有的操作都需要进过 RBAC 验证过后才能使用。这样充分保证了系统安全性。





RBAC 概念

在 FleaPHP 的 RBAC 组件中,只有下列几项概念需要理解:



除了上述三个概念,要想 RBAC 系统能够正常工作,还需要用户信息管理器、角色信息管理器和访问控制器三个部件。



FleaPHP 中已经实现了上述三个部件,所以开发者要做的功能就比较简单了。





使用 RBAC

FleaPHP 中提供了 FLEA_Com_RBAC、FLEA_Com_RBAC_UsersManager 和 FLEA_Com_RBAC_RolesManager 三个部件,以及 FLEA_Dispatcher_Auth 调度器。

其中,FLEA_Com_RBAC_UsersManager 提供用户信息存储服务,而 FLEA_Com_RBAC_RolesManager 提供角色信息存储服务。FLEA_Com_RBAC 则和 FLEA_Dispatcher_Auth 结合,一起提供了访问控制能力

下面我们来看看 RBAC 到底怎么工作的。

修改应用程序设置

使用访问控制功能,首先需要修改应用程序设置。让应用程序使用 FLEA_Dispatcher_Auth 调度器,而不是默认的 FLEA_Dispatcher_Simple 调度器。

<?php

require('FLEA/FLEA.php');
set_app_inf('dispatcher', 'FLEA_Dispatcher_Auth');
/**
* ...
* 其他初始化代码
* ...
*/

run();

?>

FLEA_Dispatcher_Auth 调度器和 FLEA_Dispatcher_Simple 调度器的基本功能一样。但在调用控制器动作方法前,FLEA_Dispatcher_Auth 调度器会通过 FLEA_Com_RBAC 组件获取保存在 session 中的用户角色信息,然后再读取控制器访问控制表(ACT)。最后调用 FLEA_Com_RBAC::check() 方法检查用户拥有的角色是否可以访问这个控制器及要调用控制器动作。

验证通过,则控制器动作方法会被调用,否则将显示错误信息,或者调用应用程序设置dispatcherAuthFailedCallback 指定的错误处理程序

准备控制器的 ACT 文件

设置好应用程序后,接下来要做的就是为控制器准备 ACT 文件

ACT 文件控制器文件同名,并且保存在同一个目录下,只是扩展名为 .act.php。例如控制器Controller_Default 的文件名是 Controller/Default.php,那么该控制器的 ACT 文件名就是 Controller/Default.act.php。

ACT 文件内容通常使用下面的格式

<?php

return array(
'allow' => 'POWER_USER, SYSTEM_ADMIN',

'actions' => array(
'remove' => array(
'allow' => 'SYSTEM_ADMIN',
),

'create' => array(
'deny' => 'SYSTEM_ADMIN',
),
),
);

?>

可以看到,ACT 文件只是单纯的返回一个数组。这个数组遵循下面的格式

array(
'allow' => '允许访问该控制器角色名',
'deny' => '禁止访问该控制器角色名',

'actions' => array(
'动作名' => array(
'allow' => '允许访问该动作的角色名',
'deny' => '禁止访问该动作的角色名',
),
// .... 更动作
),
);

在上面的格式中,角色名可以是个,例如“POWER_USER, MANAGER”。只需要用“,”分隔角色名就可以了。

通常,我们只需要为控制器指定 allow 或者 deny 就可以了。但有时候我们要允许个角色都可以访问该控制器,但该控制器中的特定方法只允许上述角色中部分角色可以访问。这时,我们可以通过'动作名' => array('allow' => '角色名', 'deny' => '角色名') 的方式来指定该控制器动作特有的 ACT。

为了便于开发,FleaPHP 预定义了几个角色,分别是:


  • RBAC_EVERYONE:表示任何用户(不管该用户是否具有角色信息
  • RBAC_HAS_ROLE:表示具有任何角色的用户(该用户必须有角色信息
  • RBAC_NO_ROLE:表示不具有任何角色的用户
  • RBAC_NULL:表示该设置没有值

 特别注意,上述四个预定义角色并不是字符串,而是常量。因此必须以 'allow' => RBAC_EVERYONE 这样方式使用。并且不能和其他角色混用,例如 'allow' => RBAC_EVERYONE . ', POWER_USER' 就是错误的。

验证规则

在验证时,首先从 session 中取出用户的角色信息。取出来的角色信息是一个数组数组中每一个项为用户具有的一个角色。例如:

$userRoles = array(
'POWER_USER',
'MANAGER',
);

然后取出控制器的 ACT,再按照下列规则进行验证:


  1. 如果 ACT 的 allow 为 RBAC_EVERYONE,则进行下列检查:
    1. 如果 ACT 的 deny 为 RBAC_NULL,则表示表示允许任何角色访问,验证结果为 true;
    2. 如果 ACT 的 deny 为 RBAC_NO_ROLE,则表示用户只要具有角色信息,就可以访问。因此如果用户的角色信息为空白,则验证结果为 false,否则验证结果为true;
    3. 如果 ACT 的 deny 为 RBAC_HAS_ROLE,则表示用户只要具有角色信息,就不允许访问。因此如果用户的角色信息为空白,则验证结果为 true,否则验证结果为false;
    4. 如果 ACT 的 deny 为 RBAC_EVERYONE,则表示这个 ACT 存在冲突(因为 allow 和 deny 都为 RBAC_EVERYONE);
    5. 检查用户角色名是否出现在 deny 指定的角色名中,如果有,则验证结果为 false,否则验证结果为 true。

  1. 如果 ACT 的 allow 为 RBAC_HAS_ROLE,则表示用户只要具有角色信息,就可以访问。因此如果用户的角色信息为空白,则验证结果为 false,否则验证结果为 true;

  1. 如果 ACT 的 allow 为 RBAC_NO_ROLE,则表示用户只要具有角色信息,就不允许访问。因此如果用户的角色信息为空白,则验证结果为 true,否则验证结果为false;

  1. 如果 ACT 的 allow 为 RBAC_NULL,则进行下列检查:
    1. 如果 ACT 的 deny 为 RBAC_NULL,则表示 ACT 既没有设置允许访问的角色,也没有设置拒绝访问的角色,这时候假定为允许访问,所以验证结果为 true;
    2. 如果 ACT 的 deny 为 RBAC_NO_ROLE,则表示用户只要具有角色信息,就可以访问。因此如果用户的角色信息为空白,则验证结果为 false,否则验证结果为true;
    3. 如果 ACT 的 deny 为 RBAC_HAS_ROLE,则表示用户只要具有角色信息,就不允许访问。因此如果用户的角色信息为空白,则验证结果为 true,否则验证结果为false;
    4. 如果 ACT 的 deny 为 RBAC_EVERYONE,则表示拒绝任何角色访问,验证结果为 false;
    5. 5) 检查用户用户角色名是否出现在 deny 指定的角色名中,如果有,则验证结果为 false,否则验证结果为 true。

  1. 验证进行到这里时,ACT 的 allow 必然是角色名,因此只要用户具有的角色名在 allow 指定的角色名中,验证结果就为true,否则验证结果为false。

ACT 示例

之所有进行这么复杂的验证,是考虑到样的验证需求。看看下面几个例子:


  • 只要具有角色,就允许访问:

array(
'allow' => RBAC_HAS_ROLE
);

  • 只要具有角色,就不允许访问:

array(
'deny' => RBAC_HAS_ROLE
);

  • 用户具有角色,并且没有 POWER_USER 角色时,允许访问:

array(
'allow' => RBAC_HAS_ROLE,
'deny' => 'POWER_USER'
)

  • 用户只要没有 MANAGER 角色,就允许访问:

array(
'deny' => 'MANAGER',
)

  • 用户具有 POWER_USER 和 MANAGER 角色时允许访问,但具有 SYSTEM_ADMIN 角色时拒绝访问。对于这个 ACT 定义,如果用户的角色为 ‚POWER_USER, GUEST‘,则允许访问。如果用户的角色为 ‚POWER_USER, SYSTEM_ADMIN‘,则不允许访问。因为 deny 的优先级总是大于 allow。

array(
'allow' => 'POWER_USER, MANAGER',
'deny' => 'SYSTEM_ADMIN',
)

上面虽然只说了对控制器的验证,但对控制器动作的验证规则是完全相同的。只是只有当用户被允许访问控制器时,才会对要访问的控制器动作进行验证。这种机制提供非常高的灵活性,例如:

<?php

return array(
'allow' => 'POWER_USER, SYSTEM_ADMIN',

'actions' => array(
'remove' => array(
'allow' => 'SYSTEM_ADMIN',
),

'create' => array(
'deny' => 'SYSTEM_ADMIN',
),
),
);

?>

用户只要具有 POWER_USER 和 SYSTEM_ADMIN 两个角色之一,就可以访问这个控制器。但只有当用户具有 SYSTEM_ADMIN 角色时,才允许使用控制器的 remove 动作。反之,如果用户具有 SYSTEM_ADMIN 角色,就不允许使用控制器的 create 动作。

 特别注意,不管是 allow 还是 deny,只要用户具有的角色有其中之一符合条件就会判定该规则有效。例如 'allow' => 'POWER_USER, SYSTEM_ADMIN' 只要用户具有 POWER_USER 和 SYSTEM_ADMIN 两个角色之一,就算作允许访问。而不需要用户同时具有 POWER_USER 和 SYSTEM_ADMIN 角色。

角色信息存储服务

准备好控制器的 ACT 后,我们还需要使用角色信息存储服务,来管理应用程序中会用到的角色信息

首先,我们要建立如下的数据表(假定使用 MySQL)。这个数据表很简单,每行记录存储一个角色。

CREATE TABLE `roles` (
`role_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`rolename` VARCHAR( 32 ) NOT NULL ,
`created` INT NULL ,
`updated` INT NULL
);

然后我们在应用程序中就可以从 FLEA_Com_RBAC_RolesManager 派生一个对象来管理角色信息了:

load_class('FLEA_Com_RBAC_RolesManager');
class MyRolesManager extends FLEA_Com_RBAC_RolesManager
{
var $tableName = 'roles';
var $primaryKey = 'role_id';
}

$rolesManager =& get_singleton('MyRolesManager');
/* @var $rolesManager MyRolesManager */
$role = array('rolename' => 'SYSTEM_ADMIN');
$rolesManager->create($role);
$role = array('rolename' => 'POWER_USER');
$rolesManager->create($role);
$role = array('rolename' => 'MANAGER');
$rolesManager->create($role);

事实上,FLEA_Com_RBAC_RolesManager 是一个表数据入口对象,所以可以直接使用 create()、find() 等方法来添加、查询角色信息

用户信息存储服务

只有角色信息,RBAC 还无法工作。我们还需要用户信息存储服务。这些服务由 FLEA_Com_RBAC_UsersManager 来提供。首先建立存储用户信息数据表(仍然假定使用 MySQL):

CREATE TABLE `users` (
`user_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR( 32 ) NOT NULL ,
`password` VARCHAR( 64 ) NOT NULL ,
`email` VARCHAR( 128 ) NOT NULL ,
`created` INT NULL ,
`updated` INT NULL
);

 特别注意,存储用户信息数据表中,至少需要用户名、密码和电子邮件地址三个字段

然后我们就可以很方便的存储和查询用户信息了:

load_class('FLEA_Com_RBAC_UsersManager');
class MyUsersManager extends FLEA_Com_RBAC_UsersManager
{
var $tableName = 'users';
var $primaryKey = 'user_id';
}

$usersManager =& get_singleton('MyUsersManager');
/* @var $usersManager MyUsersManager */

$user = array(
'username' => 'dualface',
'password' => '12345678',
'email' => 'dualface@gmail.com',
);
$usersManager->create($user);

上面的代码会建立一个用户名为 dualface 的用户,而密码明文是 12345678。你也许会奇怪,难道密码不需要加密存储吗?

实际上,FLEA_Com_RBAC_UsersManager 为你自动完成了该项工作。FLEA_Com_RBAC_UsersManager 在建立一个用户或者更新一个用户时,对于密码字段都是特别处理的。所以程序只需要提供密码明文就行了,而不需要自己加密。不过同时也要注意在更新用户时,不要更新密码字段。而是应该使用FLEA_Com_RBAC_UsersManager::changePassword() 方法来修改用户账户的密码

FLEA_Com_RBAC_UsersManager 采用什么加密方法存储密码,是由 FLEA_Com_RBAC_UsersManager::$encodeMethod 变量决定的。默认为 PWD_CRYPT,即使用 crypt() 函数加密。可用的加密方式还有 PWD_MD5(存储用 md5() 函数编码后的密码)和 PWD_CLEARTEXT(存储密码明文)。由于不同的加密方式,生成的密码长度都不同,所以建立用户信息数据表时,密码字段的长度为 64,而不是常用的 32。

 特别注意,crypt() 函数加密密码超过了 32 个字符,因此一定要确保用户信息表的密码字段有足够的长度。

如果用户信息表的用户名、密码和电子邮件地址三个字段不是默认的 username、password 和 email,那么要分别通过FLEA_Com_RBAC_UsersManager::$usernameField、FLEA_Com_RBAC_UsersManager::$passwordField 和 FLEA_Com_RBAC_UsersManager::$emailField 变量来指定。

FLEA_Com_RBAC_UsersManager 虽然也是一个表数据入口,但是提供了 findByUserId()、findByUsername、findByEmail 等便利的方法。请参考 API 文档中的 FLEA_Com_RBAC_UsersManager ,了解所有便利方法

用户指定角色

有了用户和角色信息,我们还需要将用户信息和角色信息关联起来。这仍然是通过 FLEA_Com_RBAC_UsersManager 来完成。

首先也是建立一个数据表,用来存储用户和角色之间的关联关系

CREATE TABLE `roles_users` (
`user_id` INT NOT NULL ,
`role_id` INT NOT NULL ,
PRIMARY KEY ( `user_id` , `role_id` )
);

接下来修改我们的 FLEA_Com_RBAC_UsersManager 继承

load_class('FLEA_Com_RBAC_UsersManager');

class MyUsersManager extends FLEA_Com_RBAC_UsersManager
{
var $tableName = 'users';
var $primaryKey = 'user_id';
var $rolesFields = 'roles';

var $manyToMany = array(
'tableClass' => 'MyRolesManager',
'mappingName' => 'roles',
'joinTable' => 'roles_users',
);
}

新增加的一个 MANY_TO_MANY 用于将用户和角色关联起来。**注意 MANY_TO_MANY 关联的 mappingName 选项一定要和 $rolesFields 变量的值一样。否则用 FLEA_Com_RBAC_UsersManager::fetchRoles() 无法取得用户的角色信息

不过只是这样的定义,还不能实际使用。我们看一段实际使用代码

<?php
require('FLEA/FLEA.php');
// 假定数据连接信息保存在 APP/Config/DSN.php 文件
register_app_inf('APP/Config/DSN.php');
// 由于没有调用 run() 来启动 MVC 模式,所以需要自行初始化 FleaPHP
__FLEA_PREPARE();

load_class('FLEA_Com_RBAC_RolesManager');
class MyRolesManager extends FLEA_Com_RBAC_RolesManager
{
var $tableName = 'roles';
var $primaryKey = 'role_id';
}

load_class('FLEA_Com_RBAC_UsersManager');
class MyUsersManager extends FLEA_Com_RBAC_UsersManager
{
var $tableName = 'users';
var $primaryKey = 'user_id';

var $manyToMany = array(
'tableClass' => 'MyRolesManager',
'mappingName' => 'roles',
'joinTable' => 'roles_users',
);
}

$usersManager =& get_singleton('MyUsersManager');
/* @var $usersManager MyUsersManager */

// 取出用户
$user = $usersManager->findByUsername('dualface');
// 清空现有的角色信息
$user[$usersManager->rolesField] = array();

// 取出 POWER_USER 角色
$rolesManager =& get_singleton('MyRolesManager');
/* @var rolesManager MyRolesManager */
$role = $rolesManager->find(array('rolename' => 'POWER_USER'));
// 指定给用户
$user[$usersManager->rolesField][] = $role[$rolesManager->primaryKey];

// 取出 MANAGER 角色
$role = $rolesManager->find(array('rolename' => 'MANAGER'));
// 指定给用户
$user[$usersManager->rolesField][] = $role[$rolesManager->primaryKey];

// 保存修改后的用户信息
$usersManager->update($user);

// 重新从数据库读取用户信息,确定为用户指定的角色信息已经保存到数据
$user = $usersManager->findByUsername('dualface');
dump($user);

运行这段代码,可以看到为用户指定的角色信息,确实保存到数据库了:

通常,我们会在应用程序中提供一个管理界面,用于管理用户信息,并且可以为用户指定角色。这样的管理界面似下图:

上图中的权限实际上就是可以给用户指定的角色,只不过换了一个称谓而已。

用户登录

由于要使用 RBAC 组件进行访问控制,所以我们的用户登录部分要写几行代码常见的登录代码如下:

<?php
require('FLEA/FLEA.php');
// 假定数据连接信息保存在 APP/Config/DSN.php 文件
register_app_inf('APP/Config/DSN.php');
// 由于没有调用 run() 来启动 MVC 模式,所以需要自行初始化 FleaPHP
__FLEA_PREPARE();

load_class('FLEA_Com_RBAC_RolesManager');
class MyRolesManager extends FLEA_Com_RBAC_RolesManager
{
var $tableName = 'roles';
var $primaryKey = 'role_id';
}

load_class('FLEA_Com_RBAC_UsersManager');
class MyUsersManager extends FLEA_Com_RBAC_UsersManager
{
var $tableName = 'users';
var $primaryKey = 'user_id';

var $manyToMany = array(
'tableClass' => 'MyRolesManager',
'mappingName' => 'roles',
'joinTable' => 'roles_users',
);
}

/**
* 模拟登录
*/
login('dualface', '12345678');

/**
* 处理用户登录
*/
function login($username, $password) {
$usersManager =& get_singleton('MyUsersManager');
/* @var $usersManager MyUsersManager */

// 验证用户名和密码是否正确
$user = $usersManager->findByUsername($username);
if (!$user || !$usersManager->checkPassword($password, $user[$usersManager->passwordField])) {
echo "Username invalid or password mismatch.";
exit;
}

// 获取用户角色信息
$roles = $usersManager->fetchRoles($user);

// 获得 FLEA_Com_RBAC 组件实例
$rbac =& get_singleton('FLEA_Com_RBAC');
/* @var $rbac FLEA_Com_RBAC */

// 为了降低服务器负担,我们只在 session 中存储用户ID和用户
$sessionUser = array(
'USERID' => $user[$usersManager->primaryKey],
'USERNAME' => $user[$usersManager->usernameField],
);

// 将用户ID、用户名和角色信息保存到 session
$rbac->setUser($sessionUser, $roles);

// 登录成功
echo "Login successed, contents of session:";
dump($_SESSION);
}

?>

上面的代码中第一次出现了对 FLEA_Com_RBAC 的使用。通常我们只需要用到 FLEA_Com_RBAC::setUser()方法。这个方法用户信息和对应的角色信息保存到 session。为了节约服务器资源,我们要尽量减少保存在 session 中的内容

运行这个脚本,可以看到如下的输出

大家在开发自己的应用程序时,基本上可以把 login() 函数内容照搬过去。

用户注销

处理用户注销非常简单,通常用 session_destroy() 销毁 session 数据就可以了。如果只想清除用户登录信息,而不影响 session 中的其他信息,可以用下面两行代码

$rbac =& get_singleton('FLEA_Com_RBAC');
$rbac->clearUser();




实现访问控制

实际上,做完上面几个步骤,我们的 RBAC 已经可以工作了。你可以尝试登录系统,然后访问那些受到保护的控制器。然后再从系统注销后,重新访问受保护的控制器

目前,FleaPHP 的 RBAC 在处理 ACT 上,还不够灵活。每个控制器的 ACT 都是从文件载入的,而不是从数据库。但有聪明的开发者已经想出了变通的做法。

那就是把从数据库获取控制器 ACT 的代码写在控制器的 .act.php 文件中,例如:

APP\Controller\MyController.act.php

<?php

$modelACT =& get_singleton('Model_ControllerACTProvider');
/* @var $modelACT Model_ControllerACTProvider */
return $modelACT->getACT('MyController');

?>

当然,我们还要实现一个 Model_ControllerACTProvider 表数据入口:

<?php

load_class('FLEA_Db_TableDataGateway');

class Model_ControllerACTProvider extends FLEA_Db_TableDataGateway
{
var $tableName = 'controller_acts';
var $primaryKey = 'controller_name';

function getACT($controllerName) {
$row = parent::find(array($this->primaryKey => strtoupper($controllerName)));
return unserialize($row['act']);
}

function setACT($controllerName, $ACT) {
$row = array(
$this->primaryKey => strtoupper($controllerName),
'act' => serialize($ACT)
);
return parent::create($row);
}
}

?>

以及一个数据表:

CREATE TABLE `controller_acts` (
`controller_name` VARCHAR( 32 ) NOT NULL ,
`act` TEXT NOT NULL ,
`created` INT NULL ,
`updated` INT NULL ,
PRIMARY KEY ( `controller_name` )
)

这样一来,我们就可以在数据库中保存控制器的 ACT 了。