同福

做个用户管理系统(23)——授权模式的设计和使用【20201210】

介绍

介绍

今天福哥带着大家来把会员中心(就是开放给登录用户的功能集中管理模块)的基础框架搭建一下。所谓的基础框架包括了会员中心的导航菜单,还有每个已知功能页面的视图模型以及功能页面的基本权限控制。

这里面有个问题,就是首页应该是公开的(任何人都可以访问),而会员中心里面的功能则必须有用户登录才能操作,并且有用户登录之后login和register都不应该再可以访问到,一个SystemPageController无法满足三种不同的需求,怎么办?

这里就需要一个权限模块来针对不同的资源路径使用不同的权限机制。福哥建立了一个permission模块,用来处理权限分配的逻辑,包括登录授权功能(login)、登录状态功能(status)、无授权功能(nothing)、非登录授权功能(nologin)四种授权模式。

因为PHP是脚本语言,如果一个PHP程序里的代码太多的话会影响整体性能,所以我们在设计程序的时候尽量将不同模块用到的功能分开来写,避免代码集中到一起的问题。福哥将登录状态的功能从user模块分离到了login模块里面了,这样在大多数情况下需要获取登录状态,但不需要做其他操作,提高TFUMS系统的性能。

模型login

createToken

public function createToken(int $userID, bool $remember=false):string {
    $tfredis = $this->tfphp->getDatabase()->getRedis();

    $token = md5(microtime());
    $tokenKey = "token_". $token;
    if($tfredis->set($tokenKey, $userID)
        && $tfredis->expire($tokenKey, ($remember) ? 60*60*24*7 : 60*60*8)){

        return $token;
    }

    return null;
}

getIDByToken

public function getIDByToken(string $token):int {
    $tfredis = $this->tfphp->getDatabase()->getRedis();

    $tokenKey = "token_". $token;

    return intval($tfredis->get($tokenKey));
}

clearToken

public function clearToken(string $token):bool {
    $tfredis = $this->tfphp->getDatabase()->getRedis();

    $tokenKey = "token_". $token;

    return $tfredis->delete($tokenKey);
}

detectLoginStatus

public function detectLoginStatus(){
    $login = new login($this->tfphp);
    $cookie = $this->tfphp->getRequest()->cookie;
    $id = $cookie->get("tfums_id");
    $token = $cookie->get("tfums_token");

    try{
        $idInToken = $login->getIDByToken($token);
        if($id == $idInToken){
            $this->loginStatus = new loginStatus();
            $this->fillEntityByTable($this->loginStatus, "user", array($id));
            if($this->loginStatus->userID > 0){

                return true;
            }
        }
    }
    catch(\TypeError $e){ }

    return false;
}

getLoginStatus

public function getLoginStatus():?loginStatus {

    return $this->loginStatus;
}

模型permission

detect

private function detect(){
    $this->login->detectLoginStatus();
}

match

private function match($pattern):bool {
    if(preg_match("/". $pattern. "/", $this->requestUri)){

        return true;
    }

    return false;
}

login

private function login(){
    $this->detect();
    $loginStatus = $this->login->getLoginStatus();
    if(!($loginStatus != null && $loginStatus->userID > 0)){
        $this->tfphp->getResponse()->location($this->tfphp->getRequest()->server->BASE_URI. "login.htm");
    }
}

nologin

private function nologin(){
    $this->detect();
    $loginStatus = $this->login->getLoginStatus();var_dump($this->requestUri);
    if($loginStatus != null && $loginStatus->userID > 0){
        $this->tfphp->getResponse()->location($this->tfphp->getRequest()->server->BASE_URI. "member/");
    }
}

status

private function status(){
    $this->detect();
}

nothing

private function nothing(){

}

process

protected function process(){
    if($this->match("\/member\/")){
        $this->login();
    }
    else if($this->match("(login|register)\.htm")){
        $this->nologin();
    }
    else{
        $this->nothing();
    }
}

getLoginStatus

public function getLoginStatus():?loginStatus {

    return $this->login->getLoginStatus();
}

讲解

模型login

createToken

创建用户登录状态的token

getIDByToken

通过用户登录状态的token获取登录用户ID

clearToken

清除用户登录状态的token(注销操作)

detectLoginStatus

探测用户登录状态,使用loginStatus实体来保存用户登录状态信息

getLoginStatus

获取loginStatus实体对象

模型permission

detect

调用login::detectLoginStatus方法探测用户登录状态

match

使用正则表达式检查当前资源路径是否符合匹配规则

login

登录授权模式,如果当前没有用户登录则跳转至登录页面

nologin

非登录授权模式,如果当前有用户登录则跳转至会员中心首页

status

只探测登录状态,不做处理

nothing

不探测登录状态

process

重载基类的process方法,将user_process留给业务控制器对象重载

getLoginStatus

获取loginStatus实体对象

使用

在SystemAPIController和SystemPageController两个中间层控制器的process方法里面调用permission对象,实现权限控制。

df791a2cb0293983.jpg

总结

今天福哥带着童鞋们解决了TFUMS系统的授权模式问题。这个授权模式在不同的系统里面,有着不同的设计思路,在不同的工程师手里也有着各种各样的解决方案,福哥只是把其中一种介绍给了大家。

下一课,福哥将带着大家完成修改个人资料功能,敬请期待~~