0x7OpenResty系列:Openresty最佳案例| 第8篇:RBAC介绍,SQL和redis的模块工具类

举报
云享专家 发表于 2019/09/30 14:33:50 2019/09/30
【摘要】 在Openresty中怎么连接数据库,怎么查询的SQL语句,在之前的文章已将讲述过了。根据用户ID获取用户的权限的功能是一个使用率极高的功能,所以考虑将这个功能模块化。

RBAC介绍

RBAC(基于角色的访问控制,基于角色的访问控制),用户基于角色的访问权限控制。简单地说,一个用户拥有多个角色,每个一个角色拥有多个权限。这样,就构造成用户角色-权限的授权模型在这种模型中,用户与角色之间,角色与权限之间,一般都是多对多的关系如图所示。:

3.jpg

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脚本,即给用户ID1的用户关联角色,角色并关联权限:

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');


在本案例中,需要根据用户表中的标识获取该标识对应的权限。首先根据用户id获取该用户对应的角色,再根据根据该角色获取相应的权限,往往一个用户具有多个角色,而角色又有多个权限。某些查询userId1的用户的权限的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)
  end
 
  local 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_id
  res, 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) do
    if name == "permission" then
          table.insert(permissions, 1, value)
        end
 
     end
   end
 return permissions
end
 
local _M = {
    select_user_permission= select_user_permission
}
 
return _M


在上面的代码中,有一个select_user_permissionuser_id)方法,该方法根据用户名获取该用户的权限。并来存在一个表类型的localPermission = {}中。

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,浏览器显示如下的内容:

/用户/单位启用

tokentool

在之前的文章中提到了如何使用Openresty连接redis,并操作redis。几个方法:

·      close_redisred)通过连接池的方式释放一个连接

·      connect()连接redis

·      has_tokentokenredis中存在token与否

·      get_user_idtoken)根据令牌获取用户id

·      set_permissionsuser_idpermissions)根据userid设置权限

·      get_permissionsUSER_ID)根据用户标识获取权限

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 = 6379
 
local 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 then
        return false
    end
    --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
    --end
    return red
end
 
function has_token(token)
    local red = connect()
    if red == false then
        return false
    end
 
    local res, err = red:get(token)
    if not res then
        return false
    end
    close_redis(red)
    return true
end
 
function set_permissions(user_id,permissions)
  if (permissions==null) or( permissions==ngx.null) then
     return false
  end
  local str = cjson.encode(permissions)
  ngx.log(ngx.ERR,"set redis p:"..str)
  local red=connect()
  if red== false then
     return false
  end
  local ok, err = red:set(user_id,str)
  if not ok then
     return false
  end
  return true
end
 
function get_permissions(user_id)
  local red=connect()
  if red== false then
     return false
  end
  local res, err = red:get(user_id)
  if (not res) or (res == ngx.null) then
     return
  end
  ngx.log(ngx.ERR,"get redis p:"..res);
  local permissions=cjson.decode(res)
  return permissions
end
 
function 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 then
   ngx.say("ok")
else
   ngx.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,浏览器显示:

 

原创作者:方志朋

方志朋简介:SpringCloud中国社区联合创始人,博客访问量突破一千万,爱好开源,热爱分享,活跃于各大社区,保持着非常强的学习驱动力,终身学习践行者,终身学习受益者。目前就职于国内某家知名互联网保险公司,担任DEVOPS工程师,对微服务领域和持续集成领域研究较深,精通微服务框架SpringCloud


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。