From abb59406ee11092e8225fbb3d77027e94a368a7a Mon Sep 17 00:00:00 2001 From: wanglei <34475144@qqcom> Date: Tue, 11 Nov 2025 19:00:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0oauth2.0=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=B9=B6=E5=AF=B9?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E8=BF=9B=E8=A1=8C=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=85=AC=E9=92=A5=E5=92=8C?= =?UTF-8?q?=E7=A7=81=E9=92=A5=E7=94=9F=E6=88=90=E7=9B=B8=E5=85=B3=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=92=8C=E4=BD=BF=E7=94=A8=E7=9A=84=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/system/system.conf | 8 + src/api/oauth/oauth.lua | 8 +- src/service/oauth/oauth.lua | 156 ++++++++++++-- src/test/test.lua | 385 ++++++++++++++++++++++++++++++++-- src/test/testRBAC.lua | 2 +- src/util/rbac.lua | 93 -------- src/util/rsa.lua | 80 +++++++ src/validator/oauth/oauth.lua | 11 +- 8 files changed, 616 insertions(+), 127 deletions(-) delete mode 100644 src/util/rbac.lua create mode 100644 src/util/rsa.lua diff --git a/conf/system/system.conf b/conf/system/system.conf index 7e346a2..3e37172 100644 --- a/conf/system/system.conf +++ b/conf/system/system.conf @@ -46,4 +46,12 @@ location /api/system/roles { location /api/system/users { access_by_lua_file '${APP_PATH}/src/auth/jwt-auth.lua'; content_by_lua_file '${APP_PATH}/src/api/system/user.lua'; +} + +###################################################### +### oauth2.0 + openIDC 接口文件处理 ### +###################################################### +#用户认证登陆相关 +location /api/oauth/v2 { + content_by_lua_file '${APP_PATH}/src/api/oauth/oauth.lua'; } \ No newline at end of file diff --git a/src/api/oauth/oauth.lua b/src/api/oauth/oauth.lua index 2f33ce4..5e2c1de 100644 --- a/src/api/oauth/oauth.lua +++ b/src/api/oauth/oauth.lua @@ -11,7 +11,7 @@ local oauthService = require("service.oauth.oauth") --定义相关路由,前端接口url地址 local routes = { -------------------------------------------- - -------------OAuth2.0认证相关路由配置-------------- + ------------ OAuth2.0认证相关路由配置 --------- -------------------------------------------- --获取授权码 { @@ -25,6 +25,12 @@ local routes = { methods = { "POST" }, handler = oauthService.token, }, + --通过用户名和密码进行验证 + { + paths = { "/api/oauth/v2/login" }, + methods = { "POST" }, + handler = oauthService.userinfo, + }, --根据Access-Token获取相应用户的账户信息 { paths = { "/api/oauth/v2/userinfo" }, diff --git a/src/service/oauth/oauth.lua b/src/service/oauth/oauth.lua index 8f6fb92..e1f81ba 100644 --- a/src/service/oauth/oauth.lua +++ b/src/service/oauth/oauth.lua @@ -8,6 +8,7 @@ local authDao = require("dao.oauth.oauth") local validator = require("validator.oauth.oauth") local cjson = require("cjson.safe") local token = require("util.uuid") +local jwt = require "resty.jwt" local _M = {} @@ -18,29 +19,156 @@ function _M:authorize() --获取请求数据 local body_data = ngx.req.get_body_data() -- 验证数据是否符合json - local ok = validatorJson.validatorAuthorize(body_data) + local ok = validator.validatorAuthorize(body_data) --验证失败则返回 if not ok then local result = resp:json(0x000001) resp:send(result) return end + -- end --根据授权码获取Access-Token function _M:token() --读取请求体的数据 ngx.req.read_body() - --获取请求数据 - local body_data = ngx.req.get_body_data() - -- 验证数据是否符合json - local ok = validatorJson.validatorToken(body_data) - --验证失败则返回 - if not ok then - local result = resp:json(0x000001) - resp:send(result) - return + ----获取请求数据 + --local body_data = ngx.req.get_body_data() + ---- 验证数据是否符合json + --local ok = validator.validatorToken(body_data) + ----验证失败则返回 + --if not ok then + -- local result = resp:json(0x000001) + -- resp:send(result) + -- return + --end + + -- 1. 解析请求参数(支持 form-data 和 json) + local content_type = ngx.req.get_headers()["Content-Type"] or "" + local args = {} + if string.find(content_type, "application/json") then + local body = ngx.req.get_body_data() + if not body then + return ngx.exit(ngx.HTTP_BAD_REQUEST) + end + args = cjson.decode(body) + else + -- 默认解析 form-urlencoded + args = ngx.req.get_post_args() end + + -- 2. 校验必填参数 + local required = { + grant_type = "refresh_token", + refresh_token = true, + client_id = true + } + if args.grant_type ~= required.grant_type then + return ngx.say(cjson.encode({ + error = "unsupported_grant_type", + error_description = "grant_type must be 'refresh_token'" + })) + end + if not args.refresh_token or not args.client_id then + return ngx.say(cjson.encode({ + error = "invalid_request", + error_description = "missing required parameters" + })) + end + + -- 4. 校验 Refresh Token 有效性 + local refresh_token = args.refresh_token + local client_id = args.client_id + + -- 4.1 检查是否在黑名单(已吊销) + local is_revoked, err = red:get("refresh_blacklist:" .. refresh_token) + if is_revoked == "1" then + return ngx.say(cjson.encode({ + error = "token_revoked", + error_description = "refresh_token has been revoked" + })) + end + + -- 4.2 校验客户端合法性(client_id 与 client_secret 匹配,仅后端客户端需要 secret) + local client_secret = args.client_secret or "" + local stored_secret, err = red:get("client:" .. client_id) + if not stored_secret or stored_secret == ngx.null then + return ngx.say(cjson.encode({ + error = "invalid_client", + error_description = "client_id not found" + })) + end + -- 机密客户端(如后端服务)必须验证 secret + if stored_secret ~= "public" and stored_secret ~= client_secret then + return ngx.say(cjson.encode({ + error = "invalid_client", + error_description = "client_secret invalid" + })) + end + + -- 4.3 验证 Refresh Token 签名(假设 Refresh Token 是 JWT 格式) + local refresh_jwt = jwt:verify(jwt_secret, refresh_token) + if not refresh_jwt.valid then + return ngx.say(cjson.encode({ + error = "invalid_grant", + error_description = "refresh_token invalid: " .. (refresh_jwt.reason or "unknown") + })) + end + + -- 4.4 校验 Token 绑定关系(client_id 必须与 JWT 中一致) + if refresh_jwt.payload.client_id ~= client_id then + return ngx.say(cjson.encode({ + error = "invalid_grant", + error_description = "refresh_token not bound to client_id" + })) + end + + -- 5.1 吊销旧 Refresh Token(加入黑名单,设置与原有效期一致的过期时间) + local ttl = refresh_jwt.payload.exp - ngx.time() + if ttl > 0 then + red:setex("refresh_blacklist:" .. refresh_token, ttl, "1") + end + + local access_token_ttl = 10 * 60 --十分钟 + local refresh_token_ttl = 7 * 24 * 3600 --7天 + -- 5.2 生成新 Access Token + local access_payload = { + sub = refresh_jwt.payload.sub, -- 用户ID + client_id = client_id, + scope = refresh_jwt.payload.scope or "", + exp = ngx.time() + access_token_ttl, + jti = ngx.md5(ngx.time() .. math.random() .. client_id) -- 唯一标识 + } + local new_access_token = jwt:sign(jwt_secret, { + header = { typ = "JWT", alg = "HS256" }, + payload = access_payload + }) + + -- 5.3 生成新 Refresh Token(滚动刷新) + local refresh_payload = { + sub = refresh_jwt.payload.sub, + client_id = client_id, + scope = refresh_jwt.payload.scope or "", + exp = ngx.time() + refresh_token_ttl, + jti = ngx.md5(ngx.time() .. math.random() * 1000 .. client_id) + } + local new_refresh_token = jwt:sign(jwt_secret, { + header = { typ = "JWT", alg = "HS256" }, + payload = refresh_payload + }) + + -- 6. 返回结果 + ngx.say(cjson.encode({ + access_token = new_access_token, + token_type = "Bearer", + expires_in = access_token_ttl, + refresh_token = new_refresh_token, + refresh_expires_in = refresh_token_ttl, + scope = access_payload.scope, + issued_at = ngx.time(), + jti = access_payload.jti + })) end --根据Access-Token获取相应用户的账户信息 @@ -50,7 +178,7 @@ function _M:userinfo() --获取请求数据 local body_data = ngx.req.get_body_data() -- 验证数据是否符合json - local ok = validatorJson.validatorJson(body_data) + local ok = validator.validatorJson(body_data) --验证失败则返回 if not ok then local result = resp:json(0x000001) @@ -66,7 +194,7 @@ function _M:logout() --获取请求数据 local body_data = ngx.req.get_body_data() -- 验证数据是否符合json - local ok = validatorJson.validatorJson(body_data) + local ok = validator.validatorJson(body_data) --验证失败则返回 if not ok then local result = resp:json(0x000001) @@ -82,7 +210,7 @@ function _M:refresh() --获取请求数据 local body_data = ngx.req.get_body_data() -- 验证数据是否符合json - local ok = validatorJson.validatorJson(body_data) + local ok = validator.validatorJson(body_data) --验证失败则返回 if not ok then local result = resp:json(0x000001) @@ -98,7 +226,7 @@ function _M:checklogin() --获取请求数据 local body_data = ngx.req.get_body_data() -- 验证数据是否符合json - local ok = validatorJson.validatorJson(body_data) + local ok = validator.validatorJson(body_data) --验证失败则返回 if not ok then local result = resp:json(0x000001) diff --git a/src/test/test.lua b/src/test/test.lua index f242f95..54f8d6e 100644 --- a/src/test/test.lua +++ b/src/test/test.lua @@ -8,6 +8,7 @@ local helpers = require("share.helpers") local jsonschema = require("jsonschema") local cjson = require("cjson.safe") local redis = require("share.redis") +local jwt = require("resty.jwt") --[[ local workerId = 0 -- 假设当前机器的ID是1,范围在[0, 31]之间 @@ -19,23 +20,381 @@ local id = snow:generateUniqueId()-- 生成ID --max =a and b or c--a?b:c -local openssl = require("openssl") +--local openssl = require("openssl") +-- +---- 生成RSA密钥对 +--local key = openssl.pkey.new { +-- algorithm = "RSA", +-- rsa = { +-- bits = 2048 -- 密钥长度 +-- } +--} +-- +---- 导出私钥和公钥 +--local private_key_pem = key:pem() -- 获取私钥的PEM格式 +--local public_key_pem = key:public_key():pem() -- 获取公钥的PEM格式 +-- +---- 打印密钥 +--ngx.say("Private Key:"..private_key_pem) +--ngx.say("\nPublic Key:"..public_key_pem) +-- +local openssl = require "resty.openssl" +local digest = require "resty.openssl.digest" +local pkey = require "resty.openssl.pkey" +local str = require "resty.string" --- 生成RSA密钥对 -local key = openssl.pkey.new { - algorithm = "RSA", - rsa = { - bits = 2048 -- 密钥长度 +local x509 = require "resty.openssl.x509" +--local pem = require "resty.openssl.pem" +--local rand = require "resty.openssl.rand" + +local user_id = "11" +local client_id = "aaaasddd" +local current_time = ngx.time() +local code = ngx.md5(user_id .. client_id .. current_time .. math.random()) +ngx.say("code:"..code) + +ngx.say("-----") + +-- 生成密钥对 +local function generate_rsa_keys(length) + -- 生成2048位RSA密钥对 + local key, err = pkey.new({ + type = "RSA", + bits = length or 2048 + }) + + -- 提取公钥 + local pub_pem = key:to_PEM("public") + -- 提取私钥 + local priv_pem = key:to_PEM("private") + + if not priv_pem or not pub_pem then + return nil, nil, "转换 PEM 格式失败: " .. (err or "未知错误") + end + + return pub_pem, priv_pem, nil +end + +-- 2. 验签核心函数 +-- 参数: +-- pub_key: 公钥对象(由 load_public_key 生成) +-- data: 原始数据(字符串或文件路径) +-- signature: 签名(Base64 编码字符串) +-- algorithm: 签名算法(如 "sha256" 对应 RSA-SHA256) +local function verify(pub_key, data, signature, algorithm) + -- 处理原始数据(支持字符串或文件) + local data_str + if type(data) == "string" then + -- 判断是否为文件路径 + if string.find(data, "^/") or string.find(data, "^%.%/") then + local file, err = io.open(data, "rb") + if not file then + return false, "读取数据文件失败: " .. (err or "未知错误") + end + data_str = file:read("*a") + file:close() + else + data_str = data -- 直接使用字符串数据 + end + else + return false, "原始数据格式错误(需字符串或文件路径)" + end + + -- 初始化摘要算法(与签名时一致) + local md, err = digest.new(algorithm) + if not md then + return false, "初始化摘要算法失败: " .. (err or "未知错误") + end + + -- 计算数据摘要 + md:update(data_str) + + -- 解码签名(Base64 转二进制) + -- 注意:JWT 等场景可能用 Base64Url,需先转换为标准 Base64 + local sig_bin, err = ngx.decode_base64(signature) + if not sig_bin then + return false, "签名 Base64 解码失败: " .. (err or "未知错误") + end + + -- 执行验签(默认使用 RSA_PKCS1_PADDING,与签名时一致) + local ok, err = pub_key:verify(sig_bin, md) + if not ok then + return false, "验签失败: " .. (err or "未知原因") + end + return true +end + +-- 3. 示例:验证字符串签名(RSA-SHA256) +local function example() + -- 原始数据(签名前的内容) + local original_data = "hello, world" + + -- 1. 生成密钥对 + local pub_key, priv_key, err = generate_rsa_keys(2048) + if err then + return --nil, "密钥生成失败: " .. err + end + + ngx.say(pub_key) + ngx.say("-----") + ngx.say(priv_key) + ngx.say("-----") + + -- 创建签名 Base64 编码,实际应从外部获取,如请求参数) + -- 使用SHA256算法 + local digest1 = digest.new("sha256") + -- 更新签名上下文,提供数据以供签名 + digest1:update(original_data) + -- 完成签名 + local signature, err = digest1:final(priv_key) + if not signature then + ngx.log(ngx.ERR, "Failed to generate signature: ", err) + return + end + ngx.say("签名文件: ", signature) + local decode_date = ngx.encode_base64(signature) + ngx.say("签名加密后(Base64):", decode_date) + + local pkeyNew, err = pkey.new(pub_key) + if not pkeyNew then + return + end + + -- 验签(算法与签名时一致,这里用 SHA256) + local ok, err = verify(pkeyNew, original_data, decode_date, "sha256") + if ok then + ngx.say("验签成功:数据未被篡改,签名有效") + else + ngx.say("验签失败:" .. err) + end +end + +-- 执行示例 +example() + +do + return +end + +-- 公钥加密(用于生成测试数据) +local function rsa_encrypt(pub_key, plaintext) + -- + local pkey, err = pkey.new(pub_key) + if not pkey or not plaintext then + return nil, "参数错误" + end + + local oaep_params = { + oaep_md = "sha256", -- 对应pkey.lua中的opts.oaep_md + mgf1_md = "sha256", -- 对应pkey.lua中的opts.mgf1_md + label = nil } + + local RSA_PKCS1_OAEP_PADDING = "4" + local ciphertext, err = pkey:encrypt(plaintext, RSA_PKCS1_OAEP_PADDING, oaep_params) + if not ciphertext then + return nil, "加密失败: " .. (err or "未知错误") + end + -- 返回Base64编码的密文(便于传输存储) + return ngx.encode_base64(ciphertext), nil +end + +-- 私钥解密(核心实现) +local function rsa_decrypt(priv_key, encrypted_data) + local pkey, err = pkey.new(priv_key) + if not pkey or not encrypted_data then + return nil, "参数错误(私钥或密文为空)" + end + + -- 1. 先解码Base64密文 + local ciphertext, err = ngx.decode_base64(encrypted_data) + if not ciphertext then + return nil, "Base64解码失败: " .. (err or "无效密文") + end + -- 2. 设置解密填充方式(必须与加密时一致) + local oaep_params = { + oaep_md = "sha256", -- 对应pkey.lua中的opts.oaep_md + mgf1_md = "sha256", -- 对应pkey.lua中的opts.mgf1_md + label = nil + } + + local RSA_PKCS1_OAEP_PADDING = "4" + -- 3. 执行解密 + local result, err = pkey:decrypt(ciphertext, RSA_PKCS1_OAEP_PADDING, oaep_params) + + if not result then + return nil, "解密返回空结果" + end + + return result, nil -- 返回解密后的原始数据 +end + +-- 完整测试流程 +local function test_rsa_crypto() + -- 1. 生成密钥对 + local pub_key, priv_key, err = generate_rsa_keys(2048) + if err then + return nil, "密钥生成失败: " .. err + end + + ngx.say(pub_key) + ngx.say("-----") + ngx.say(priv_key) + ngx.say("-----") + -- 2. 原始数据 + local original_text = "这是一段需要加密的敏感数据:123456" + ngx.say("原始数据: ", original_text) + + -- 3. 公钥加密 + local encrypted_data, err = rsa_encrypt(pub_key, original_text) + if err then + return nil, "加密失败: " .. err + end + ngx.say("公钥加密后(Base64): ", encrypted_data) + + -- 4. 私钥解密 + local decrypted_text, err = rsa_decrypt(priv_key, encrypted_data) + if err then + return nil, "私钥解密失败: " .. err + end + ngx.say("私钥解密后: ", decrypted_text) + ngx.say("私钥解密后hex: ", str.to_hex(decrypted_text)) + + -- 5. 验证结果 + if decrypted_text ~= original_text then + return nil, "解密结果不匹配原始数据" + end + + ------------------------------- + -- 要签名的数据 + local md5val = original_text --ngx.md5(original_text) -- 使用MD5作为摘要算法,你也可以使用其他如sha256等 + + --[[ + -- 创建签名 + -- 使用SHA256算法 + local digest1 = digest.new("sha256") + -- 更新签名上下文,提供数据以供签名 + digest1:update(md5val) + -- 完成签名 + local signature, err = digest1:final(priv_key) + if not signature then + ngx.log(ngx.ERR, "Failed to generate signature: ", err) + return + end + ngx.say("签名文件: ", signature) + local decode_date = ngx.encode_base64(signature) + ngx.say("签名加密后(Base64):", decode_date) + --]] + ---- 生成签名------------------ + -- 使用SHA256算法 + local digestG = digest.new("sha256") + -- 更新签名上下文,提供数据以供签名 + digestG:update(md5val) + -- 完成签名 + local signature, err = digestG:final(priv_key) + if not signature then + ngx.log(ngx.ERR, "Failed to generate signature: ", err) + return + end + ngx.say("签名数据: ", signature) + local encode_date = ngx.encode_base64(signature) + ngx.say("签名数据加密后(Base64):", encode_date) + ---- 验证签名------------------ + local pubK, err = pkey.new(pub_key) + if not pubK then + return nil, "参数错误(私钥或密文为空)" + end + local decode_date = ngx.decode_base64(encode_date) + ngx.say("签名数据解密后(Base64):", decode_date) + local is_valid = pubK:verify(encode_date, md5val) -- 使用与签名相同的摘要算法进行验证 + if is_valid then + ngx.say("Signature is valid.") + else + ngx.say("Signature is invalid.") + end + ----------------------------------- + return true, "加密解密验证成功" +end + +-- 1. 生成密钥对 +local pub_key, priv_key, err = generate_rsa_keys(2048) +if err then + return nil, "密钥生成失败: " .. err +end + +-- 私钥和公钥 +local private_key = priv_key +local public_key = pub_key + +-- 创建JWT的payload +local payload = { + iss = "https://example.com", + sub = "1234567890", + name = "John Doe", + iat = os.time(), + exp = os.time() + 3600 } --- 导出私钥和公钥 -local private_key_pem = key:pem() -- 获取私钥的PEM格式 -local public_key_pem = key:public_key():pem() -- 获取公钥的PEM格式 +-- 使用私钥生成JWT +local jwt_obj = jwt:sign(private_key, { + header = { + type = "JWT", + alg = "RS256" + }, + payload = payload +}) +if not jwt_obj then + ngx.say("Failed to generate JWT") + return +end --- 打印密钥 -ngx.say("Private Key:"..private_key_pem) -ngx.say("\nPublic Key:"..public_key_pem) +ngx.say("Generated JWT: ", jwt_obj) + +-- 使用公钥校验JWT +local decoded, res = jwt:verify(public_key, jwt_obj) +if not decoded then + ngx.say("Failed to verify JWT: ", cjson.encode(res)) + return +end + +ngx.say("JWT is valid: ", cjson.encode(decoded)) + +--local original_text = "这是一段需要加密的敏感数据:123456" +--local key = x509.PKey() +--key:generate_key("rsa", 2048) -- 生成 RSA 密钥,2048 位长度 +-- +--local cert = x509.X509() +--cert:set_version(2) -- 设置证书版本为 2 (RFC 5280) +--cert:set_serial_number({0x01, 0x02, 0x03}) -- 设置序列号 +--cert:set_issuer_name([[ +-- C=US, ST=Texas, L=Houston, O=Example Inc, OU=IT, CN=example.com +-- ]]) -- 设置颁发者名称 +--cert:set_subject_name([[ +-- C=US PARTICULARITY, ST=Texas, L=Houston, O=Example Inc, OU=IT, CN=example.com +-- ]]) -- 设置主题名称(与颁发者相同或不同) +--cert:set_pubkey(key) -- 设置公钥 +-- +--cert:set_not_before(os.time()) -- 设置生效时间(当前时间) +--cert:set_not_after(os.time() + 60*60*24*365) -- 设置过期时间(一年后) +-- +--cert:sign(key, "sha256") -- 使用私钥和 SHA256 签名证书 +-- +--local key_pem = pem.to_pem(key) -- 将密钥转换为 PEM 格式 +--local cert_pem = pem.to_pem(cert) -- 将证书转换为 PEM 格式 + +-- 输出或保存到文件 +--ngx.say(key_pem) +--ngx.say(cert_pem) + +-- 执行测试 +local success, msg = test_rsa_crypto() +if success then + ngx.say("测试成功: \n", msg) +else + ngx.status = 500 + ngx.say("测试失败: \n", msg) +end +ngx.say("-----") --[[ local radix = require("resty.radixtree") @@ -156,7 +515,7 @@ for i = 1, #uid do table.insert(result, string.sub(charset, rand, rand)) end -print(generate_12char_uuid()) -- 示例输出:aB3eF7hJ9kL2 +--print(generate_12char_uuid()) -- 示例输出:aB3eF7hJ9kL2 --[[ local args = ngx.req.get_uri_args() diff --git a/src/test/testRBAC.lua b/src/test/testRBAC.lua index 418e8ff..9ab6dae 100644 --- a/src/test/testRBAC.lua +++ b/src/test/testRBAC.lua @@ -4,7 +4,7 @@ --- DateTime: 2025/11/3 11:38 --- -local rbac = require("util.rbac") +local rbac = require("util.rsa") -- 创建RBAC实例 local permission_system = rbac.new() diff --git a/src/util/rbac.lua b/src/util/rbac.lua deleted file mode 100644 index 35475dc..0000000 --- a/src/util/rbac.lua +++ /dev/null @@ -1,93 +0,0 @@ ---- ---- Generated by EmmyLua(https://github.com/EmmyLua) ---- Created by admin. ---- DateTime: 2025/11/3 11:31 ---- - -local RBAC = {} -RBAC.__index = RBAC - --- RBAC模型初始化 -function RBAC.new() - local self = setmetatable({}, RBAC) - self.users = {} -- 用户表: {user_id = {roles = {role1, role2}}} - self.roles = {} -- 角色表: {role_name = {permissions = {perm1, perm2}}} - self.permissions = {} -- 权限表: {perm_name = {resource = "", action = ""}} - return self -end - --- 添加权限 -function RBAC:add_permission(perm_name, resource, action) - self.permissions[perm_name] = { - resource = resource, - action = action - } -end - --- 添加角色并分配权限 -function RBAC:add_role(role_name, permissions) - self.roles[role_name] = { - permissions = permissions or {} - } -end - --- 分配角色给用户 -function RBAC:assign_role(user_id, role_name) - if not self.users[user_id] then - self.users[user_id] = {roles = {}} - end - table.insert(self.users[user_id].roles, role_name) -end - --- 检查用户权限 -function RBAC:check_permission(user_id, resource, action) - local user = self.users[user_id] - if not user then return false end - - for _, role_name in ipairs(user.roles) do - local role = self.roles[role_name] - if role then - for _, perm_name in ipairs(role.permissions) do - local permission = self.permissions[perm_name] - if permission and permission.resource == resource and permission.action == action then - return true - end - end - end - end - return false -end - --- 获取用户所有权限 -function RBAC:get_user_permissions(user_id) - local user_permissions = {} - local user = self.users[user_id] - if not user then return user_permissions end - - for _, role_name in ipairs(user.roles) do - local role = self.roles[role_name] - if role then - for _, perm_name in ipairs(role.permissions) do - table.insert(user_permissions, self.permissions[perm_name]) - end - end - end - return user_permissions -end - --- 添加角色 ---_, err = permit.AddPolicy(roleName, roleId, action) - --- 赋予用户角色 ---_, err = permit.AddRoleForUser(user, roleName) - --- 查看具有某角色的所有用户 ---res, err = permit.GetUsersForRole(roleName) - --- 移除用户具有的角色 ---_, err = permit.DeleteRoleForUser(user, roleName) - --- 移除角色,跟角色相关联的用户都被移除 ---_, err = permit.DeleteRole(roleName) - -return RBAC \ No newline at end of file diff --git a/src/util/rsa.lua b/src/util/rsa.lua new file mode 100644 index 0000000..0323258 --- /dev/null +++ b/src/util/rsa.lua @@ -0,0 +1,80 @@ +local pkey = require "resty.openssl.pkey" +local str = require "resty.string" + +local _M = {} + +-- 生成密钥对 +function _M:generate_rsa_keys(length) + -- 生成2048位RSA密钥对 + local key, err = pkey.new({ + type = "RSA", + bits = length or 2048 + }) + + -- 提取公钥 + local pub_pem = key:to_PEM("public") + -- 提取私钥 + local priv_pem = key:to_PEM("private") + + if not priv_pem or not pub_pem then + return nil, nil, "转换 PEM 格式失败: " .. (err or "未知错误") + end + + return pub_pem, priv_pem, nil +end + +-- 公钥加密(用于生成测试数据) +function _M:rsa_encrypt(pub_key, plaintext) + -- + local pkey, err = pkey.new(pub_key) + if not pkey or not plaintext then + return nil, "参数错误" + end + + local oaep_params = { + oaep_md = "sha256", -- 对应pkey.lua中的opts.oaep_md + mgf1_md = "sha256", -- 对应pkey.lua中的opts.mgf1_md + label = nil + } + + local RSA_PKCS1_OAEP_PADDING = "4" + local ciphertext, err = pkey:encrypt(plaintext, RSA_PKCS1_OAEP_PADDING ,oaep_params) + if not ciphertext then + return nil, "加密失败: " .. (err or "未知错误") + end + -- 返回Base64编码的密文(便于传输存储) + return ngx.encode_base64(ciphertext), nil +end + +-- 私钥解密(核心实现) +function _M:rsa_decrypt(private_key, encrypted_data) + + local pkey, err = pkey.new(private_key) + + if not pkey or not encrypted_data then + return nil, "参数错误(公钥或密文为空)" + end + + -- 1. 先解码Base64密文 + local ciphertext, err = ngx.decode_base64(encrypted_data) + if not ciphertext then + return nil, "Base64解码失败: " .. (err or "无效密文") + end + -- 2. 设置解密填充方式(必须与加密时一致) + local oaep_params = { + oaep_md = "sha256", -- 对应pkey.lua中的opts.oaep_md + mgf1_md = "sha256", -- 对应pkey.lua中的opts.mgf1_md + label = nil + } + + local RSA_PKCS1_OAEP_PADDING = "4" + -- 3. 执行解密 + local result, err = pkey:decrypt(ciphertext, RSA_PKCS1_OAEP_PADDING, oaep_params) + if not result then + return nil, "解密返回空结果" + end + + return result, nil -- 返回解密后的原始数据 +end + +return _M \ No newline at end of file diff --git a/src/validator/oauth/oauth.lua b/src/validator/oauth/oauth.lua index eaa084c..f08813d 100644 --- a/src/validator/oauth/oauth.lua +++ b/src/validator/oauth/oauth.lua @@ -10,11 +10,12 @@ local _M = {} -- 定义一个JSON Schema local schemaAuth = { {type = "object", properties = { - {name = "username", type = "string"}, - {name = "password", type = "string"}, - {name = "captcha", type = "string"}, - {name = "checkKey", type = "string"}, - }, required = {"username", "password"}} + {name = "responseType", type = "string", default = "code"}, + {name = "clientId", type = "string", minLength = 10}, + {name = "redirectUri", type = "string", minLength = 8}, + {name = "state", type = "string"}, + {name = "scope", type = "string"}, + }, required = {"responseType", "clientId", "redirectUri"}} } --获取授权码