增加oauth2.0相关协议接口,并对配置文件进行修改,增加公钥和私钥生成相关函数和使用的测试例子
This commit is contained in:
parent
e62ba6a9d7
commit
abb59406ee
|
|
@ -47,3 +47,11 @@ 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';
|
||||
}
|
||||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
80
src/util/rsa.lua
Normal file
80
src/util/rsa.lua
Normal file
|
|
@ -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
|
||||
|
|
@ -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"}}
|
||||
}
|
||||
|
||||
--获取授权码
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user