在开发一些网站时,有些模块需要用户登录后方可访问,没有登录的话则跳转到登录页提示用户登录或注册。假设现在已有可判断用户是否登录的方法,那么如何在该模块的所有Controller和action中加上登录权限判断呢?
很容易想到的是所有需要登录权限判断的Controller都继承一个父Controller,在这个父Controller中添加一个方法,在未登录的情况下使用以下方式跳转。
1
|
return
$this
->
redirect
(
)
->
toRoute
(
'login'
)
;
|
这种方法还是比较麻烦,好在Zend Framework 2提供了很强大的事件机制,我们可以在真正进入action前就判断权限,然后做出相应的动作。
首先打开需要校验登录的模块的Module.php文件,里面有一个onBootstrap方法:
1
2
3
4
5
|
public
function
onBootstrap
(
MvcEvent
$e
)
{
$eventManager
=
$e
->
getApplication
(
)
->
getEventManager
(
)
;
$moduleRouteListener
=
new
ModuleRouteListener
(
)
;
$moduleRouteListener
->
attach
(
$eventManager
)
;
}
|
事件执行的先后顺序如下表:
事件名 | 对应MvcEvent中的常量 | 说明 |
bootstrap | MvcEvent::EVENT_BOOTSTRAP | 应用程序启动,创建ViewManager,此时session已能获取 |
route | MvcEvent::EVENT_ROUTE | 匹配路由,此时能够通过MvcEvent->getRouteMatch()获得匹配到的路由 |
dispatch | MvcEvent::EVENT_DISPATCH | 将匹配中的路由请求分发到controller和action |
dispatch.error | MvcEvent::EVENT_DISPATCH_ERROR | 请求分发到action失败 |
render | MvcEvent::EVENT_RENDER | 准备数据渲染视图层,此时可以MvcEvent->getViewModel()来获取视图 |
render.error | MvcEvent::EVENT_RENDER_ERROR | 渲染失败 |
finish | MvcEvent::EVENT_FINISH | 整个请求任务结束 |
如果使用session的方式来判断是否登录,那么在onBootstrap方法中就可以做到了。这里要做的就是如果没登录,我们就要将页面跳转到登录页面,当然,如果用户本来访问的就是登录页,就不能再跳转了,以免出现死循环。这个例子中我们配置的登录页路由名称为login,对应的Controller和acion分别为Application\Controller\PassportController和loginAction,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
function
onBootstrap
(
MvcEvent
$e
)
{
$eventManager
=
$e
->
getApplication
(
)
->
getEventManager
(
)
;
$moduleRouteListener
=
new
ModuleRouteListener
(
)
;
$moduleRouteListener
->
attach
(
$eventManager
)
;
//如果未登录
if
(
!
isset
(
$_SESSION
[
'username'
]
)
)
{
$response
=
$e
->
getResponse
(
)
;
$eventManager
->
attach
(
MvcEvent::
EVENT_ROUTE
,
function
(
$event
)
use
(
$response
)
{
if
(
$event
->
getRouteMatch
(
)
->
getMatchedRouteName
(
)
!=
'login'
)
{
//如果当前请求的不是登录页,才做跳转,如果有logout页也应该加上
$url
=
$event
->
getRouter
(
)
->
assemble
(
[
'controller'
=
>
'Application\Controller\Passport'
,
'action'
=
>
'login'
]
,
[
'name'
=
>
'login'
]
)
;
$response
->
getHeaders
(
)
->
addHeaderLine
(
'Location'
,
$url
)
;
$response
->
setStatusCode
(
302
)
;
$response
->
sendHeaders
(
)
;
$event
->
stopPropagation
(
)
;
}
}
,
-
100
)
;
return
$response
;
}
}
|
还有一种方法,可以不做页面跳转,而是直接在路由时将原来匹配上的路由修改成登录页的路由,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
function
onBootstrap
(
MvcEvent
$e
)
{
$eventManager
=
$e
->
getApplication
(
)
->
getEventManager
(
)
;
$moduleRouteListener
=
new
ModuleRouteListener
(
)
;
$moduleRouteListener
->
attach
(
$eventManager
)
;
//如果未登录
if
(
!
isset
(
$_SESSION
[
'username'
]
)
)
{
$eventManager
->
attach
(
MvcEvent::
EVENT_ROUTE
,
function
(
$event
)
use
(
$response
)
{
if
(
$event
->
getRouteMatch
(
)
->
getMatchedRouteName
(
)
!=
'login'
)
{
$routeMatch
=
new
RouteMatch
(
[
'controller'
=
>
'Admin\Controller\Administrator'
,
'action'
=
>
'login'
]
)
;
$routeMatch
->
setMatchedRouteName
(
'login'
)
;
$event
->
setRouteMatch
(
$routeMatch
)
;
}
,
-
100
)
;
}
}
|
这种方法有个问题在于虽然展示了登录页,但url并没有变成登录页的url(/login),而是保留了用户请求的url,这样就会对搜索引擎不够友好,爬虫爬任何url都会返回登录页,且并没有发生302跳转,搜索引擎可能会认为全是重复页面。