整个系统的第一个层次已经开发完毕,已经有简单的中控,登录、退出、延迟登录时长、黑名单、数据层封装、验证层封装、RSA加解密、Redis等功能,还缺获取个人、角色按钮权限、角色菜单权限功能。角色按钮权限以及角色菜单权限等明后天开发,今天先开发个人信息接口以便测试使用。
一、个人信息接口开发
1、添加路由
在根目录下route文件夹中的app.php文件中加入以下代码:
Route::post('member/personal_data','permission.Member/personalData');
2、代码开发
在根目录下app文件夹下的controller文件夹中创建文件夹并命名为permission,然后在permission文件夹中创建控制器并命名为Member,然后在Member文件中继承Base总控制后并创建personalData方法。具体代码如下:
<?php
/**
* 个人信息控制-也就是登录者信息及角色对外方法
* User: 龙哥·三年风水
* Date: 2024/10/30
* Time: 14:18
*/
namespace app\controller\permission;
use app\controller\Base;
class Member extends Base
{
/**
* 获取个人信息
* User: 龙哥·三年风水
* Date: 2024/10/30
* Time: 14:23
* @ return \think\response\Json
*/
public function personalData(){
$data = [];
$data['username'] = $this->username;
$data['avatar'] = $this->avatar;
$data['email'] = $this->email;
$data['realname'] = $this->realname;
return succ('操作成功',$data);
}
}
二、nginx黑名单开发
因nginx自身是没有redis及rsa扩展使用的,所以我安装的不是nginx,而是openresty,他是有带resty扩展的。如果不会安装openresty,可以参照《centos7 二进制安装openresty》、《配置openresty》以及《openresty安全机制-白名单》三篇文章进行安装配置。
1、引入扩展
--引入Redis
local redis = require "resty.redis";
--引入rsa
local rsa = require "resty.rsa";
2、配置redis
--Redis链接ip
local ip = "172.20.36.144"
--Redis链接端口
local port = 6379
--Redis连接密码
local pass = "QXtr@@PxjoLenGon"
3、在对应的Redis库里面添加公钥字符串
我不管是在应用程序里面还是在本机制里面,都采用的是Redis第0号库的black-list库中哈,所有的非正常访问系统的IP都在black-list库中。
4、开发lua对应的Redis授权
--鉴权Redis
local function connAuth()
local red = redis:new();
local connCount, err = red:connect(ip, port);
if not connCount then
ngx.say("failed to connect: ", err)
close_redis(red)
return
end
red:set_timeouts(2000);
local ok, err = red:auth(pass)
if not ok then
ngx.say("failed to auth: ", err)
close_redis(red)
return
end
return red
end
5、开发lua对应的Redis关闭
--关闭Redis
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
6、开发获取请求者IP
--获取请求者IP
local function getIp()
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr
end
return clientIP
end
--获取用户访问IP
local clientIP = getIp();
--封禁token时间(秒)
local token_block_time= 120
--指定token访问频率计数最大值(次)
local token_max_count = 3
7、开发空方法
--如果数据为空的情况下
local function is_empty(value)
if type(value) == "table" then
return next(value) == nil
elseif type(value) == "string" then
return #value == 0
elseif type(value) == "nil" then
return true
end
return false
end
8、过滤特殊字符串
--过滤特殊字符串(只保留字母与数字,字母不区分大小写)
local function filter_special_chars(s)
local ss = {}
local k = 1
while true do
if k > #s then break end
local c = string.byte(s,k)
if not c then break end
if c<192 then
if (c>=48 and c<=57) or (c>= 65 and c<=90) or (c>=97 and c<=122) then
table.insert(ss, string.char(c))
end
k = k + 1
end
end
return table.concat(ss)
end
9、组合开发黑名单机制
--引入Redis
local redis = require "resty.redis";
--引入rsa
local rsa = require "resty.rsa";
--Redis链接ip
local ip = "172.20.36.144"
--Redis链接端口
local port = 6379
--Redis连接密码
local pass = "QXtr@@PxjoLenGon"
--鉴权Redis
local function connAuth()
local red = redis:new();
local connCount, err = red:connect(ip, port);
if not connCount then
ngx.say("failed to connect: ", err)
close_redis(red)
return
end
red:set_timeouts(2000);
local ok, err = red:auth(pass)
if not ok then
ngx.say("failed to auth: ", err)
close_redis(red)
return
end
return red
end
--关闭Redis
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
--获取请求者IP
local function getIp()
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr
end
return clientIP
end
--获取用户访问IP
local clientIP = getIp();
--封禁token时间(秒)
local token_block_time= 120
--指定token访问频率计数最大值(次)
local token_max_count = 3
--如果数据为空的情况下
local function is_empty(value)
if type(value) == "table" then
return next(value) == nil
elseif type(value) == "string" then
return #value == 0
elseif type(value) == "nil" then
return true
end
return false
end
--过滤特殊字符串(只保留字母与数字,字母不区分大小写)
local function filter_special_chars(s)
local ss = {}
local k = 1
while true do
if k > #s then break end
local c = string.byte(s,k)
if not c then break end
if c<192 then
if (c>=48 and c<=57) or (c>= 65 and c<=90) or (c>=97 and c<=122) then
table.insert(ss, string.char(c))
end
k = k + 1
end
end
return table.concat(ss)
end
--如果头部信息没有指定的参数或是指定参数的值无法解析,加入IP黑名单
--如果同样的头部参数键在封禁token时间内连续访问指定token访问频率计数最大值次以上,加入IP黑名单----暂时还没有开发,开发测试好后,将重新更新
local function set_blacklist()
local header = ngx.req.get_headers();
local red = connAuth();
local token, err = header["Authorization"];
-- 如果参数为空的情况下
if not token then
local res, err = red:sismember('black-list', clientIP);
if res ~= 1 then
red:sadd('black-list', clientIP)
end
close_redis(red)
return ngx.exit(401)
else
-- 如果参数值为空的情况下
if is_empty(token) then
local res, err = red:sismember('black-list', clientIP);
if res ~= 1 then
red:sadd('black-list', clientIP)
end
close_redis(red)
return ngx.exit(401)
end
-- 如果参数值采用base64解析不开的情况下
local encrypted, err = tostring(ngx.decode_base64(token))
if not encrypted then
local res, err = red:sismember('black-list', clientIP);
if res ~= 1 then
red:sadd('black-list', clientIP)
end
close_redis(red)
return ngx.exit(401)
end
-- 采用rsa技术解析token base64过后的值内容
local priv_key = '-----BEGIN PRIVATE KEY-----\n' ..red:get('priv_key_cluster_control')..'\n-----END PRIVATE KEY-----';
local priv, err = rsa:new({private_key = priv_key})
local dst, err = rsa.decrypt(priv, encrypted)
if not dst then
local res, err = red:sismember('black-list', clientIP);
if res ~= 1 then
red:sadd('black-list', clientIP)
end
close_redis(red)
return ngx.exit(401)
end
end
end
-- 查看是否在黑名单里面
local function get_blacklist()
local red = connAuth();
local res, err = red:sismember('black-list', clientIP);
if res == 1 then
close_redis(red);
return ngx.exit(401);
end
close_redis(red);
end
get_blacklist();
set_blacklist();
10、将开发好的lua文件也一并上传到网站目录中,然后更改nginx应用配置文件,然后重新启动nginx,代码如下:
server {
listen 80;
server_name controlapi.yuanlongsoftware.cn;
error_log /data/nginx/controlapi.yuanlongsoftware.cn80_error.log crit;
access_log /data/nginx/controlapi.yuanlongsoftware.cn80_acess_$logdate.log access-upstream;
lua_code_cache off;
autoindex off;
server_tokens off;
error_page 401 /401.html;
location / {
if ($request_method ~* OPTIONS) {
return 200;
}
access_by_lua_file /data/wwwroot/teach/group_control/cluster_control/rsa-cluster-control.lua;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Access-Control-Allow-Origin "$http_origin";
add_header Access-Control-Allow-Headers 'X-Requested-With,Cache-Control,Content-Type,Authorization';
add_header Cache-Control no-cache;
add_header 'Access-Control-Allow-Methods' 'GET, POST';
proxy_pass http://controlapi;
}
location = /401.html {
root "/data/wwwroot/error";
}
}
三、测试
1、登录后用token去获取一下用户信息接口,采用apipost工具测试
2、采用同一个token再次去获取一下用户信息接口,采用apipost工具再次测试
3、采用同一个token再次去获取一下用户信息接口,采用apipost工具再次测试
第二次以后,就是openresty直接返回咯,因lua去检测是发现他就在黑名单中,就直接返回。
其实后期我还会出对应的精简文章,也就是优化过后的文章。
四、提前说明
明后天将带来管理员功能开发、角色功能开发、菜单功能开发以及上传文件开发