RBAC介绍
RBAC(Role-Based Access Control,基于角色的访问控制),用户基于角色的访问权限控制。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般都是多对多的关系。如图所示:
sql_tool
在本案例中,采用的就是这种权限设计的方式。具体的sql语句脚本如下:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`name` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci
AUTO_INCREMENT=2
ROW_FORMAT=COMPACT
;CREATE TABLE role(
`id` int(11) NOT NULL AUTO_INCREMENT ,
`name` varchar(255) CHARACTER SET latin5 NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci
AUTO_INCREMENT=2
ROW_FORMAT=COMPACT
;CREATE TABLE permission(
`id` int(11) NOT NULL AUTO_INCREMENT ,
`permission` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci
AUTO_INCREMENT=3
ROW_FORMAT=COMPACT
;CREATE TABLE user_role(
`id` int(11) NOT NULL AUTO_INCREMENT ,
`user_id` int(11) NULL DEFAULT NULL ,
`role_id` int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci
AUTO_INCREMENT=2
ROW_FORMAT=COMPACT
;CREATE TABLE role_permission(
`id` int(11) NOT NULL AUTO_INCREMENT ,
`role_id` int(11) NULL DEFAULT NULL ,
`permission_id` int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci
AUTO_INCREMENT=3
ROW_FORMAT=COMPACT
;
初始化以下的sql脚本,即给用户id为1的用户关联角色,角色并关联权限:
INSERT INTO `permission` VALUES ('1', '/user/orgs');
INSERT INTO `role` VALUES ('1', 'user');
INSERT INTO `role_permission` VALUES ('1', '1', '1');
INSERT INTO `user` VALUES ('1', 'forezp');
INSERT INTO `user_role` VALUES ('1', '1', '1');
在本案例中,需要根据user表中的Id获取该Id对应的权限。首先根据userId获取该用户对应的角色,再根据根据该角色获取相应的权限,往往一个用户具有多个角色,而角色又有多个权限。比如查询userId为1 的用户的权限的sql语句如下:
SELECT a.id,a.permission from permission a ,role_permission b,role c,user_role d,user e WHERE a.id=b.permission_id and c.id=b.role_id and d.role_id=c.id and d.user_id=e.id and e.id=1"
在Openresty中怎么连接数据库,怎么查询sql语句,在之前的文章已将讲述过了。根据用户id获取用户的权限的功能是一个使用率极高的功能,所以考虑将这个功能模块化。
vim /usr/example/lualib/sql_tool.lua ,编辑加入以下的代码:
local mysql = require("resty.mysql") local function close_db(db) if not db then return end db:close()
end local function select_user_permission(user_id)local db, err = mysql:new()if not db then ngx.say("new mysql error : ", err) return end db:set_timeout(1000) local props = { host = "127.0.0.1", port = 3306, database = "test", user = "root", password = "123" }local res, err, errno, sqlstate = db:connect(props) if not res then ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) close_db(db)endlocal select_sql = "SELECT a.id,a.permission from permission a ,role_permission b,role c,user_role d,user e WHERE a.id=b.permission_id and c.id=b.role_id and d.role_id=c.id and d.user_id=e.id and e.id="..user_idres, err, errno, sqlstate = db:query(select_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local permissions={}for i, row in ipairs(res) do for name, value in pairs(row) doif name == "permission" thentable.insert(permissions, 1, value)end end end return permissions
endlocal _M = { select_user_permission= select_user_permission
} return _M
在上面的代码中,有一个select_user_permission(user_id)方法,该方法根据用户名获取该用户的权限。查出来存在一个table 类型的 local permissions={}中。
vim /usr/example/example.conf 加上以下的代码:
location ~ /sql_tool{default_type 'text/html';content_by_lua_file /usr/example/lua/test_sql_tool.lua;}
在浏览器上访问http://116.196.177.123/sql_tool,浏览器显示如下的内容:
/user/orgs
tokentool
在之前的文章讲述了如何使用Openresty连接redis,并操作redis。 这小节将讲述如何使用openresty连接redis,并写几个方法,用于存储用户的token等,并将这些信息模块化,主要有以下几个方法:
close_redis(red) 通过连接池的方式释放一个连接
connect() 连接redis
has_token(token) redis中存在token 与否
get_user_id(token) 根据token获取用户id
set_permissions(user_id,permissions) 根据userid设置权限
get_permissions(user_id)根据userid获取权限
vim /usr/example/lualib/tokentool.lua 编辑一下内容:
module("tokentool", package.seeall)
local redis = require "resty.redis"
local str = require "resty.string"
local cjson = require("cjson") local redis_host = "127.0.0.1"
local redis_port = 6379local function close_redis(red) if not red then return end local pool_max_idle_time = 10000 --毫秒 local pool_size = 100 --连接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.say("set keepalive error : ", err) end
end local function connect()local red = redis:new()red:set_timeout(1000)local ok, err = red:connect(redis_host, redis_port)if not ok thenreturn falseend--local res, err = red:auth("xiaoantimes")--if not res then-- ngx.say("failed to authenticate: ", err)-- return false--end--ok, err = red:select(1)--if not ok then-- return false--endreturn red
endfunction has_token(token)local red = connect()if red == false thenreturn falseendlocal res, err = red:get(token)if not res thenreturn falseendclose_redis(red) return true
endfunction set_permissions(user_id,permissions)if (permissions==null) or( permissions==ngx.null) thenreturn falseend local str = cjson.encode(permissions) ngx.log(ngx.ERR,"set redis p:"..str)local red=connect()if red== false thenreturn falseendlocal ok, err = red:set(user_id,str)if not ok thenreturn falseendreturn true
endfunction get_permissions(user_id)local red=connect()if red== false thenreturn falseendlocal res, err = red:get(user_id)if (not res) or (res == ngx.null) thenreturnend ngx.log(ngx.ERR,"get redis p:"..res);local permissions=cjson.decode(res) return permissions
endfunction get_user_id(token)local red = connect()local resp, err = red:get(token) if not resp then ngx.say("get msg error : ", err) return close_redis(red) end close_redis(red) return resp
end
vim /usr/example/lua/test_token_tool.lua,加上以下的内容:
local tokentool= require "tokentool"
local ret = tokentool.has_token("msg")
ngx.log(ngx.ERR,ret)
if ret == true thenngx.say("ok")
elsengx.say("oops,error")
end
在/usr/example/example.conf加上以下的内容:
location ~ /token_tool{default_type 'text/html';lua_code_cache on;content_by_lua_file /usr/example/lua/test_token_tool.lua;}
打开浏览器访问http://116.196.177.123/token_tool,浏览器显示:
ok