群控系统服务端开发模式-应用开发-业务架构逻辑开发第一轮测试

整个系统的第一个层次已经开发完毕,已经有简单的中控,登录、退出、延迟登录时长、黑名单、数据层封装、验证层封装、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去检测是发现他就在黑名单中,就直接返回。

其实后期我还会出对应的精简文章,也就是优化过后的文章。

四、提前说明

明后天将带来管理员功能开发、角色功能开发、菜单功能开发以及上传文件开发

相关推荐
KIDAKN2 小时前
RabbitMQ 初步认识
分布式·rabbitmq
pan3035074792 小时前
Kafka 和 RabbitMQ的选择
分布式·kafka·rabbitmq
hzulwy4 小时前
Kafka基础理论
分布式·kafka
明达智控技术5 小时前
MR30分布式IO在全自动中药煎药机中的应用
分布式·物联网·自动化
jakeswang6 小时前
细说分布式ID
分布式
失散137 小时前
分布式专题——1.2 Redis7核心数据结构
java·数据结构·redis·分布式·架构
王中阳Go8 小时前
头一次见问这么多kafka的问题
分布式·kafka
boonya10 小时前
Kafka核心原理与常见面试问题解析
分布式·面试·kafka
ytttr87310 小时前
PHP中各种超全局变量使用的过程
开发语言·php
KIDAKN11 小时前
RabbitMQ 重试机制 和 TTL
分布式·rabbitmq