转载请标明出处:

http://blog.csdn.net/forezp/article/details/78616738

本文出自方志朋的博客

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)
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_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 = 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,浏览器显示:

ok




扫码关注公众号有惊喜

(转载本站文章请注明作者和出处 方志朋的博客

Openresty最佳案例 | 第8篇:RBAC介绍、sql和redis模块工具类的更多相关文章

  1. Openresty最佳案例 | 第2篇:Lua入门

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616622 本文出自方志朋的博客 什么是lua Lua 是一种轻量小巧的脚本语言,用标准 ...

  2. Openresty最佳案例 | 第4篇:OpenResty常见的api

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616660 本文出自方志朋的博客 获取请求参数 vim /usr/example/exa ...

  3. Openresty最佳案例 | 第1篇:Nginx介绍

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616591 本文出自方志朋的博客 Nginx 简介 Nginx是一个高性能的Web 服务 ...

  4. Openresty最佳案例 | 第9篇:Openresty实现的网关权限控制

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616779 本文出自方志朋的博客 简介 采用openresty 开发出的api网关有很多 ...

  5. Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616714 本文出自方志朋的博客 Lua模块开发 在实际的开发过程中,不可能把所有的lu ...

  6. Openresty最佳案例 | 第6篇:OpenResty连接Mysql

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616698 本文出自方志朋的博客 centos 安装mysl Centos系统下安装my ...

  7. Openresty最佳案例 | 第5篇:http和C_json模块

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616672 本文出自方志朋的博客 http客户端 Openresty没有提供默认的Htt ...

  8. Openresty最佳案例 | 第3篇:Openresty的安装

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616645 本文出自方志朋的博客 我的服务器为一台全新的centos 7的服务器,所以从 ...

  9. Openresty最佳案例 | 汇总

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616856 本文出自方志朋的博客 目录 Openresty最佳案例 | 第1篇:Ngin ...

随机推荐

  1. jgrid 选择器 C#控件

    一.RadioButtonList html代码: <asp:RadioButtonList ID="rlPlan" runat="server" Rep ...

  2. 深入理解JavaScript系列(2):揭秘命名函数表达式

    前言 网上还没用发现有人对命名函数表达式进去重复深入的讨论,正因为如此,网上出现了各种各样的误解,本文将从原理和实践两个方面来探讨JavaScript关于命名函数表达式的优缺点. 简单的说,命名函数表 ...

  3. 用DebuggerDisplay在Visual Studio的调试器中定制类的显示方式

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:用DebuggerDisplay在Visual Studio的调试器中定制类的显示方式.

  4. 跨页面传值之QueryString

    跨页面传值常用方法 1.QueryString 2.Form-post控件传递 3.Cookies传递 4.Application传递 5.Session传递(灵活强大) 1.query传值 http ...

  5. OpenLayers 3 实现划线,画点

    //划线 this.drawLine = function (type) { //清除双击放大效果 this.map.getInteractions().item(1).setActive(false ...

  6. CRM——销售与客户

    一.销售与客户——表结构 1.客户类型 (1)公共客户(公共资源) 必备条件:没有报名: 在必备条件满足的情况下,满足以下任意条件都是公共客户: 3天没有跟进:15天没有成单. (2)我的客户 原销售 ...

  7. 使用Gulp压缩HTML和CSS

    ---恢复内容开始--- 今天我么继续压缩,但是今天的压缩和之前的不同了!可以说是第二种方法吧! 今天用Gulp来压缩HTML和CSS! 1.首先我们先来安装GUlp:先安装全局gulp 2.然后是开 ...

  8. c语言进制转化

    #include <stdio.h> // 进制转化 int main(void) { ; ; int i3 = 0x32C; printf( printf( printf("十 ...

  9. iOS中使用RNCryptor对资源文件加密

    原文:http://blog.csdn.net/chenpolu/article/details/46277587 RNCryptor源码https://github.com/RNCryptor/RN ...

  10. ViewPager应用引导界面

    如图设置的一种引导页的开启这个引用时先将图片进行一个动画当动画结束时进入到了引导页面 下面的小图片 当点击的时候ViewPager消失 再点击时ViewPager在显示出来 先看开启界面  上面的动画 ...