Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

The permission system module is a very important function for Internet products, which can control different roles to access different resources reasonably so as to achieve safe access

What models are available for permission control

ACL RBAC Indicates role-based access control

Acls are directly related to users and permissions, while RBAC is indirectly related to users and permissions through roles. So we noticed that roles are an important attribute of the RBAC system.

What is the RBAC model

Role-based Access Control (RBAC) is used to associate users with rights Based on roles. Simply put, a user has several roles, and each role has several permissions. In this way, a user – role – permission authorization model is constructed. In this model, the relationship between users and roles, roles and permissions is generally many-to-many.

Why the RBAC model

Here’s why:

Convenient user grouping

Facilitate permission assignment and reclamation

The expansion is convenient and can meet most business requirements

Before we talk about permission management, we should know that permission management has functions.

The five attributes of RBAC model are as follows:

1 User Attributes (Zhang SAN, Li Si, Wang Wu)

2 Role Attributes (Sales manager, salesperson, receptionist)

3 Relationship between users and roles (Zhang SAN is sales manager, Li Si and Wang Wu are sales)

4 Permissions (Add a customer, edit a customer, delete a customer, view a customer)

5 Relationship between Rights and Roles (The sales manager can view, add, delete, and edit customers, while the sales manager can view, add, delete, and edit customers)

An RBAC permission module must realize three functions

User management

List of users

Add user

Edit the user

Setting user Roles

Role management

The role list

Adding roles

Editing the role

Setting Role Rights

Rights management

Permissions list

Additional permissions

Edit permissions

Data table design

The users table

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
  `name` varchar(20) NOT NULL DEFAULT ' ' COMMENT 'name'.`email` varchar(30) NOT NULL DEFAULT ' ' COMMENT 'email'.`is_admin` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is it super administrator? 1: Yes, 0: No'.`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Status 1: valid 0: invalid'.`updated_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Last Update Time'.`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Insertion time',
  PRIMARY KEY (`id`),
  KEY `idx_email` (`email`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='User table';
Copy the code

Character sheet

CREATE TABLE `role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT ' ' COMMENT 'Role Name'.`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Status 1: valid 0: invalid'.`updated_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Last Update Time'.`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Insertion time',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Role table';
Copy the code

User role table

CREATE TABLE `user_role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `uid` int(11) NOT NULL DEFAULT '0' COMMENT 'user id'.`role_id` int(11) NOT NULL DEFAULT '0' COMMENT 'character ID'.`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Insertion time',
  PRIMARY KEY (`id`),
  KEY `idx_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='User Role Table';
Copy the code

Permission Details Table

CREATE TABLE `access` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(50) NOT NULL DEFAULT ' ' COMMENT 'Permission Name'.`urls` varchar(1000) NOT NULL DEFAULT ' ' COMMENT 'the json array'.`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Status 1: valid 0: invalid'.`updated_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Last Update Time'.`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Insertion time',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Permission Details Table';
Copy the code

Role permission table

CREATE TABLE `role_access` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `role_id` int(11) NOT NULL DEFAULT '0' COMMENT 'character id'.`access_id` int(11) NOT NULL DEFAULT '0' COMMENT 'authorization id'.`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Insertion time',
  PRIMARY KEY (`id`),
  KEY `idx_role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Role Permission Table';
Copy the code

User operation record form

CREATE TABLE `app_access_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` bigint(20) NOT NULL DEFAULT '0' COMMENT 'brand UID'.`target_url` varchar(255) NOT NULL DEFAULT ' ' COMMENT 'Url accessed'.`query_params` longtext NOT NULL COMMENT 'GET and POST parameters'.`ua` varchar(255) NOT NULL DEFAULT ' ' COMMENT 'access ua'.`ip` varchar(32) NOT NULL DEFAULT ' ' COMMENT 'access IP'.`note` varchar(1000) NOT NULL DEFAULT ' ' COMMENT 'JSON format remarks field'.`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='User Operation Record';
Copy the code

Code implementation

All pages of this system are required to log in to access, in the framework to join the unified verification method

public function beforeAction($action) {
	$login_status = $this->checkLoginStatus();
	if(!$login_status && !in_array( $action->uniqueId,$this->allowAllAction )  ) {
		if(Yii::$app->request->isAjax){
			$this->renderJSON([],"Not logged in, please return to user center", -302);
		}else{
			$this->redirect( UrlService::buildUrl("/user/login"));// Return to the login page
		}
		return false;
	}
	// Save all accesses to the database
	$get_params = $this->get( null );
	$post_params = $this->post( null );
	$model_log = new AppAccessLog();
	$model_log->uid = $this->current_user?$this->current_user['id'] :0;
	$model_log->target_url = isset( $_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI'] :' ';
	$model_log->query_params = json_encode( array_merge( $post_params.$get_params));$model_log->ua = isset( $_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT'] :' ';
	$model_log->ip = isset( $_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR'] :' ';
	$model_log->created_time = date("Y-m-d H:i:s");
	$model_log->save( 0 );
	/** * The logic for checking permissions is * To retrieve the role of the current login user, * to retrieve the permission relationship based on the role * to retrieve all permission links from the permission table * to determine whether the currently accessed link is in the permission list */
	// Determine whether the currently accessed link is in the permissions list
	if(!$this->checkPrivilege( $action->getUniqueId() ) ){
		$this->redirect( UrlService::buildUrl( "/error/forbidden"));return false;
	}
	return true;
}
Copy the code

Checks whether you have permission to access the specified link

public function checkPrivilege( $url ){
	// If you are the super administrator, you do not need permission judgment
	if( $this->current_user && $this->current_user['is_admin'] ){
		return true;
	}
 
	// There are some pages that do not require permission judgment
	if( in_array( $url.$this->ignore_url ) ){
		return true;
	}
 
	return in_array( $url.$this->getRolePrivilege( ) );
}
Copy the code

Obtain all the permissions of a user, retrieve the role of the specified user, obtain the permission relationship based on the role, and retrieve all permission links from the permission table

public function getRolePrivilege($uid = 0){
	if(!$uid && $this->current_user ){
		$uid = $this->current_user->id;
	}
 
	if(!$this->privilege_urls ){
		$role_ids = UserRole::find()->where([ 'uid'= >$uid ])->select('role_id')->asArray()->column();
		if( $role_ids) {// Access the permission relationship by role
			$access_ids = RoleAccess::find()->where([ 'role_id'= >$role_ids ])->select('access_id')->asArray()->column();
			// Retrieve all permission links in the permission table
			$list = Access::find()->where([ 'id'= >$access_ids ])->all();
			if( $list) {foreach( $list as $_item) {$tmp_urls = @json_decode(  $_item['urls'].true );
					$this->privilege_urls = array_merge( $this->privilege_urls,$tmp_urls); }}}}return $this->privilege_urls ;

Copy the code