I am doing an app that requires authentication. In the index page of the app, I specified access rules like this
public function accessRules() {
return array(
array('deny',
'actions'=>array('index','register','login','password'),
'users'=>array('@'),
),
array('allow',
'users'=>array('*')
),
);
}
At the first rule, the actions 'index','register','login' and 'password' are made unaccessible to authenticated users. However, I do not want to show this message
Unauthorized
You are not authorized to perform this action.
You do not have the proper credential to access this page.
If you think this is a server error, please contact the webmaster.
...to the authenticated users when they try to access those actions. Instead, I want to redirect them to another page. It would be useful if i could do something like this at the first rule
array('redirect',
'actions'=>array('index','register','login','password'),
'users'=>array('@'),
'url'=>array('home/index'),
),
Since Yii v1.1.11 you can do the same thing with a callback and a closure, and just the default classes:
array('deny',
'actions'=>array('index','register','login','password'),
'users'=>array('@'),
'deniedCallback' => function() { Yii::app()->controller->redirect(array ('/home/index')); }
),
Starting with Yii v1.1.11 CAccessRule
defines the deniedCallback
property that easily allows you to define a redirect when access is denied. I don't want to steal Iain Gray's thunder, so go upvote his answer (thanks to the commenter who alerted me to this as well).
The original answer follows.
To do this we will need to write our own classes to be used instead of CAccessRule
and CAccessControlFilter
. For CAccessRule
we just need to add one additional property:
class MyAccessRule extends CAccessRule {
public $redirect; // just add this property
}
For CAccessControlFilter
we want to make it recognize the value of this property and act upon it. To do this, we need to override the preFilter
method. Starting from the stock implementation, make a few changes:
class MyAccessControlFilter extends CAccessControlFilter {
protected function preFilter($filterChain)
{
$app=Yii::app();
$request=$app->getRequest();
$user=$app->getUser();
$verb=$request->getRequestType();
$ip=$request->getUserHostAddress();
foreach($this->getRules() as $rule)
{
if(($allow=$rule->isUserAllowed($user,
$filterChain->controller,
$filterChain->action,
$ip,
$verb))>0) // allowed
break;
else if($allow<0) // denied
{
// CODE CHANGED HERE
$request->redirect($app->createUrl($rule->redirect));
return false;
}
}
return true;
}
}
Then we also need to override the setRules
method, to instruct the filter to use the MyAccessRule
class instead of the standard CAccessRule
. Again, we modify the stock implementation by changing the line
$r=new CAccessRule;
to read
$r=new MyAccessRule;
After creating these classes, we have to also inject them into Yii's pipeline. To do this, override filterAccessControl
on the base controller class; again, taking the stock implementation as reference and making a small change:
public function filterAccessControl($filterChain)
{
$filter=new MyAccessControlFilter; // CHANGED THIS
$filter->setRules($this->accessRules());
$filter->filter($filterChain);
}
That's it! You can now take advantage of the extra functionality in any controller by supplying the new redirect
parameter to the access control filters like this:
public function accessRules() {
return array(
array('deny',
'actions'=>array('index','register','login','password'),
'users'=>array('@'),
'redirect'=>array('home/index'),
),
);
}
If you are not comfortable with subclassing Yii's core components, another option that I do not recommend is to embed both access control and redirection logic inside each controller action that you want to protect, or overriding the beforeAction
method on your controllers to cover multiple actions from one location.
this one worked for me since yii 1.1.11:
array('deny', // deny all users
'users'=>array('*'),
'deniedCallback' => $this->redirect('/')
),
or use a static method in a Class:
'deniedCallback' => array('ClassName', 'staticMethodName'),
$request->redirect($app->createUrl($rule->redirect));
Should be:
if(is_array($rule->redirect) && isset ($rule->redirect[0])){
$request->redirect($app->createUrl($rule->redirect[0]));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With