Compare commits
2 Commits
7c8a9452f2
...
2cbc0e3d3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cbc0e3d3f | ||
|
|
a43ee358d8 |
|
|
@ -26,7 +26,7 @@ http {
|
||||||
|
|
||||||
#在Nginx启动时执行的Lua代码块
|
#在Nginx启动时执行的Lua代码块
|
||||||
#oauth2.0第三方验证后将code放到共享内存中
|
#oauth2.0第三方验证后将code放到共享内存中
|
||||||
lua_shared_dict codeDict 5m;
|
lua_shared_dict codeDict 10m;
|
||||||
#init_by_lua_block {
|
#init_by_lua_block {
|
||||||
# -- 定义一个全局变量
|
# -- 定义一个全局变量
|
||||||
# ngx.log(ngx.INFO, "Initializing global variable")
|
# ngx.log(ngx.INFO, "Initializing global variable")
|
||||||
|
|
@ -65,6 +65,25 @@ http {
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#OP端点配置
|
||||||
|
location /yum/v1/.well-known/openid-configuration {
|
||||||
|
content_by_lua_block {
|
||||||
|
local cjson = require "cjson"
|
||||||
|
local config = {
|
||||||
|
issuer = "http://localhost:9080",
|
||||||
|
authorization_endpoint = "http://localhost:9080/yum/v1/oauth/v2/authorize",
|
||||||
|
token_endpoint = "http://localhost:9080yum/v1/oauth/v2/token",
|
||||||
|
userinfo_endpoint = "http://localhost:9080yum/v1/oauth/v2/userinfo",
|
||||||
|
--jwks_uri = "http://localhost:9080/jwks", -- 公钥端点(可选)
|
||||||
|
response_types_supported = {"code"},
|
||||||
|
subject_types_supported = {"public"},
|
||||||
|
id_token_signing_alg_values_supported = {"HS256"}
|
||||||
|
}
|
||||||
|
ngx.header["Content-Type"] = "application/json"
|
||||||
|
ngx.say(cjson.encode(config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#数据列表配置
|
#数据列表配置
|
||||||
include 'system/system.conf';
|
include 'system/system.conf';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ local routes = {
|
||||||
--获取授权码
|
--获取授权码
|
||||||
{
|
{
|
||||||
paths = { "/yum/v1/oauth/v2/authorize" },
|
paths = { "/yum/v1/oauth/v2/authorize" },
|
||||||
methods = { "POST" },
|
methods = { "GET", "POST" },
|
||||||
handler = oauthService.authorize,
|
handler = oauthService.authorize,
|
||||||
},
|
},
|
||||||
--根据授权码获取Access-Token
|
--根据授权码获取Access-Token
|
||||||
{
|
{
|
||||||
paths = { "/yum/v1/oauth/v2/token" },
|
paths = { "/yum/v1/oauth/v2/token" },
|
||||||
methods = { "POST" },
|
methods = { "GET", "POST" },
|
||||||
handler = oauthService.token,
|
handler = oauthService.token,
|
||||||
},
|
},
|
||||||
--通过用户名和密码进行验证
|
--通过用户名和密码进行验证
|
||||||
|
|
@ -46,7 +46,7 @@ local routes = {
|
||||||
--根据Refresh-Token刷新Access-Token
|
--根据Refresh-Token刷新Access-Token
|
||||||
{
|
{
|
||||||
paths = { "/yum/v1/oauth/v2/refresh" },
|
paths = { "/yum/v1/oauth/v2/refresh" },
|
||||||
methods = { "POST" },
|
methods = { "GET", "POST" },
|
||||||
handler = oauthService.refresh,
|
handler = oauthService.refresh,
|
||||||
},
|
},
|
||||||
--验证token是否有效
|
--验证token是否有效
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ function _M.getUser(userid)
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.getApplicationBy(client_id, redirect_uri)
|
function _M.getApplicationBy(client_id, redirect_uri)
|
||||||
|
--print("getApplicationBy client_id:", client_id, " redirect_uri:", redirect_uri)
|
||||||
return applicationDao.getApplicationByClientId(client_id, redirect_uri)
|
return applicationDao.getApplicationByClientId(client_id, redirect_uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ end
|
||||||
|
|
||||||
--根据客户端id和重定向地址获取应用程序
|
--根据客户端id和重定向地址获取应用程序
|
||||||
function _M.getApplicationByClientId(client_id, redirect_uri)
|
function _M.getApplicationByClientId(client_id, redirect_uri)
|
||||||
|
--print("getApplicationByClientId client_id:", client_id, " redirect_uri:", redirect_uri)
|
||||||
return applicationModel:where('app_id', '=', client_id):where('redirect_uris', '=', redirect_uri):get()
|
return applicationModel:where('app_id', '=', client_id):where('redirect_uris', '=', redirect_uri):get()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,42 +4,45 @@
|
||||||
--- DateTime: 2025/10/28 11:09
|
--- DateTime: 2025/10/28 11:09
|
||||||
--- 用于
|
--- 用于
|
||||||
local resp = require("util.response")
|
local resp = require("util.response")
|
||||||
local authDao = require("dao.oauth.oauth")
|
local oauthDao = require("dao.oauth.oauth")
|
||||||
local validator = require("validator.oauth.oauth")
|
local validator = require("validator.oauth.oauth")
|
||||||
local cjson = require("cjson.safe")
|
local cjson = require("cjson.safe")
|
||||||
local token = require("util.uuid")
|
local token = require("util.uuid")
|
||||||
local jwt = require "resty.jwt"
|
local jwt = require "resty.jwt"
|
||||||
|
local rsa = require("util.rsa")
|
||||||
|
|
||||||
local _M = {}
|
local _M = {}
|
||||||
|
|
||||||
--获取授权码
|
--获取授权码
|
||||||
function _M:authorize()
|
function _M:authorize()
|
||||||
--读取请求体的数据
|
|
||||||
ngx.req.read_body()
|
|
||||||
local args = ngx.req.get_uri_args()
|
local args = ngx.req.get_uri_args()
|
||||||
--获取请求数据
|
if ngx.req.get_method() == "POST" then
|
||||||
local body_data = ngx.req.get_body_data()
|
-- 读取请求体的数据
|
||||||
-- 验证json数据是否正确
|
ngx.req.read_body()
|
||||||
local ok, data = pcall(cjson.decode, body_data)
|
-- 获取请求数据
|
||||||
if not ok then
|
local body_data = ngx.req.get_body_data()
|
||||||
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
-- 验证json数据是否正确
|
||||||
end
|
local ok, data = pcall(cjson.decode, body_data)
|
||||||
-- 校验客户端请求参数
|
if not ok then
|
||||||
ok = validator.validatorAuthorize(data)
|
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||||
--验证失败则返回
|
end
|
||||||
if not ok then
|
-- 校验客户端请求参数
|
||||||
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
ok = validator.validateAuthorize(data)
|
||||||
|
--验证失败则返回
|
||||||
|
if not ok then
|
||||||
|
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- 校验 response_type 必须为 "code"(授权码模式)
|
-- 校验 response_type 必须为 "code"(授权码模式)
|
||||||
if args.response_type ~= "code" then
|
if args.response_type ~= "code" then
|
||||||
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||||
end
|
end
|
||||||
-- 校验客户端id和redirect_uri是否存在数据库
|
-- 1、校验客户端id和redirect_uri是否存在数据库
|
||||||
local code, res = authDao.getApplicationBy(args.client_id, args.redirect_uri)
|
local code, res = oauthDao.getApplicationBy(args.client_id, args.redirect_uri)
|
||||||
if code ~= 0 or not res then
|
if code ~= 0 or not res then
|
||||||
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||||
end
|
end
|
||||||
-- 验证范围
|
-- 2、验证范围
|
||||||
if args.scope then
|
if args.scope then
|
||||||
local requested_scopes = {}
|
local requested_scopes = {}
|
||||||
for scope in string.gmatch(args.scope, "%S+") do
|
for scope in string.gmatch(args.scope, "%S+") do
|
||||||
|
|
@ -47,192 +50,189 @@ function _M:authorize()
|
||||||
end
|
end
|
||||||
-- 验证范围是否允许 todo
|
-- 验证范围是否允许 todo
|
||||||
end
|
end
|
||||||
-- 判断用户登录检查 用户已登录,直接展示授权确认页;未登录则重定向到登录页
|
-- 3、判断用户登录检查 用户已登录,直接展示授权确认页;未登录则重定向到登录页
|
||||||
local user_logged_in = false
|
local user_logged_in = true
|
||||||
if not user_logged_in then
|
if not user_logged_in then
|
||||||
-- 重定向到登录页,携带当前授权请求参数(登录后跳转回来)
|
-- 重定向到登录页,携带当前授权请求参数(登录后跳转回来)
|
||||||
local login_url = "/login?redirect=" .. ngx.escape_uri(ngx_var.request_uri)
|
local login_url = "/login?redirect=" .. ngx.escape_uri(ngx.var.request_uri)
|
||||||
ngx.redirect(login_url)
|
--print("authorize login_url:", login_url)
|
||||||
|
--ngx.redirect(login_url)
|
||||||
|
local result = resp:json(ngx.HTTP_MOVED_TEMPORARILY, login_url)
|
||||||
|
resp:send(result)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
-- 4. 生成授权码(随机字符串,确保唯一性)
|
||||||
-- 4. 处理用户授权确认(用户点击"同意"后提交 POST 请求)
|
local function generate_code()
|
||||||
if ngx_req.get_method() == "POST" then
|
local str = require "resty.string"
|
||||||
local post_args = ngx_req.get_post_args()
|
local random = require "resty.random"
|
||||||
if post_args.action == "allow" then
|
local bytes = random.bytes(16)
|
||||||
-- 5. 生成授权码(随机字符串,确保唯一性)
|
return str.to_hex(bytes)
|
||||||
local function generate_code()
|
|
||||||
local str = require "resty.string"
|
|
||||||
local random = require "resty.random"
|
|
||||||
local bytes = random.bytes(16)
|
|
||||||
return str.to_hex(bytes)
|
|
||||||
end
|
|
||||||
local code = generate_code()
|
|
||||||
-- 存储授权码信息(用户ID、客户端ID、scope、生成时间)
|
|
||||||
local code_key = "auth_code:" .. code
|
|
||||||
local code_data = cjson.encode({
|
|
||||||
user_id = "123456",
|
|
||||||
client_id = args.client_id,
|
|
||||||
scope = args.scope or "",
|
|
||||||
created_at = ngx.time()
|
|
||||||
})
|
|
||||||
local shared_dict = ngx.shared.codeDict
|
|
||||||
shared_dict:set(code_key, code_data, 5 * 60)
|
|
||||||
|
|
||||||
-- 7. 重定向到客户端回调地址,携带授权码和原始 state(防 CSRF)
|
|
||||||
local redirect_url = args.redirect_uri .. "?code=" .. code .. "&state=" .. args.state
|
|
||||||
ngx.redirect(redirect_url)
|
|
||||||
return
|
|
||||||
|
|
||||||
elseif post_args.action == "deny" then
|
|
||||||
-- 用户拒绝授权,重定向到客户端并携带错误信息
|
|
||||||
local redirect_url = args.redirect_uri .. "?error=access_denied&state=" .. args.state
|
|
||||||
ngx.redirect(redirect_url)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
local code = generate_code()
|
||||||
|
--print("authorize generate_code:", code)
|
||||||
|
-- 5、存储授权码信息(用户ID、客户端ID、scope、生成时间)
|
||||||
|
local code_key = "auth_code-"..code
|
||||||
|
local code_data = cjson.encode({
|
||||||
|
user_id = "123456",
|
||||||
|
client_id = args.client_id,
|
||||||
|
request_uri = ngx.var.request_uri,
|
||||||
|
scope = args.scope or "",
|
||||||
|
created_at = ngx.time()
|
||||||
|
})
|
||||||
|
local shared_dict = ngx.shared.codeDict
|
||||||
|
shared_dict:set(code_key, code_data)
|
||||||
|
shared_dict:expire(code_key, 300) --时效性为5分钟
|
||||||
|
--print("token set shared dict key:",code_key)
|
||||||
|
-- 6. 重定向到客户端回调地址,携带授权码和原始 state(防 CSRF)
|
||||||
|
local redirect_url = args.redirect_uri .. "?code=" .. code .. "&state=" .. args.state
|
||||||
|
local rest = {}
|
||||||
|
rest.redirect_uri = args.redirect_uri
|
||||||
|
rest.code = code
|
||||||
|
rest.state = args.state
|
||||||
|
local result = resp:json(ngx.HTTP_OK, rest)
|
||||||
|
resp:send(result)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
--根据授权码获取Access-Token
|
--根据授权码获取Access-Token
|
||||||
function _M:token()
|
function _M:token()
|
||||||
--读取请求体的数据
|
|
||||||
ngx.req.read_body()
|
|
||||||
----获取请求数据
|
|
||||||
--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)
|
-- 1. 解析请求参数(支持 form-data 和 json)
|
||||||
local content_type = ngx.req.get_headers()["Content-Type"] or ""
|
local content_type = ngx.req.get_headers()["Content-Type"] or ""
|
||||||
local args = {}
|
local args = {}
|
||||||
|
--print("token content_type:", content_type)
|
||||||
if string.find(content_type, "application/json") then
|
if string.find(content_type, "application/json") then
|
||||||
local body = ngx.req.get_body_data()
|
-- 读取请求体的数据
|
||||||
if not body then
|
ngx.req.read_body()
|
||||||
|
-- 获取请求数据
|
||||||
|
local body_data = ngx.req.get_body_data()
|
||||||
|
if not body_data then
|
||||||
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||||
end
|
end
|
||||||
args = cjson.decode(body)
|
-- 验证json数据是否正确
|
||||||
|
local ok, data = pcall(cjson.decode, body_data)
|
||||||
|
if not ok then
|
||||||
|
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||||
|
end
|
||||||
|
args = data
|
||||||
else
|
else
|
||||||
-- 默认解析 form-urlencoded
|
-- 默认解析 form-urlencoded
|
||||||
args = ngx.req.get_post_args()
|
args = ngx.req.get_post_args()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 2. 校验必填参数
|
-- 2. 校验必填参数验证数据是否符合json
|
||||||
local required = {
|
local ok = validator.validateToken(args)
|
||||||
grant_type = "refresh_token",
|
--验证失败则返回
|
||||||
refresh_token = true,
|
if not ok then
|
||||||
client_id = true
|
local result = resp:json(0x000001)
|
||||||
}
|
resp:send(result)
|
||||||
if args.grant_type ~= required.grant_type then
|
return
|
||||||
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
|
end
|
||||||
|
|
||||||
-- 4. 校验 Refresh Token 有效性
|
-- 4. 校验 code 有效性
|
||||||
local refresh_token = args.refresh_token
|
local code = args.code
|
||||||
local client_id = args.client_id
|
local code_key = "auth_code-"..code
|
||||||
|
local shared_dict = ngx.shared.codeDict
|
||||||
-- 4.1 检查是否在黑名单(已吊销)
|
local code_data = shared_dict:get(code_key)
|
||||||
local is_revoked, err = red:get("refresh_blacklist:" .. refresh_token)
|
if code_data == nil then
|
||||||
if is_revoked == "1" then
|
-- code 超出时效,需要重新获取code
|
||||||
return ngx.say(cjson.encode({
|
--local result = resp:json(0x000001)
|
||||||
error = "token_revoked",
|
--resp:send(result)
|
||||||
error_description = "refresh_token has been revoked"
|
local login_url = "/login?redirect=" .. ngx.escape_uri(ngx.var.request_uri)
|
||||||
}))
|
local result = resp:json(ngx.HTTP_MOVED_TEMPORARILY, login_url)
|
||||||
|
resp:send(result)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
--print("token code_data:", code_data)
|
||||||
-- 4.2 校验客户端合法性(client_id 与 client_secret 匹配,仅后端客户端需要 secret)
|
local jsonData = cjson.decode(code_data)
|
||||||
local client_secret = args.client_secret or ""
|
-- 5、验证redirect_url地址的正确性
|
||||||
local stored_secret, err = red:get("client:" .. client_id)
|
local request_uri = jsonData.redirect_uri
|
||||||
if not stored_secret or stored_secret == ngx.null then
|
print("token request_uri:", request_uri)
|
||||||
return ngx.say(cjson.encode({
|
if request_uri ~= args.redirect_url then
|
||||||
error = "invalid_client",
|
--print("token redirect_url:", request_uri, args.redirect_url)
|
||||||
error_description = "client_id not found"
|
local login_url = "/login?redirect=" .. ngx.escape_uri(request_uri)
|
||||||
}))
|
local result = resp:json(ngx.HTTP_MOVED_TEMPORARILY, login_url)
|
||||||
end
|
resp:send(result)
|
||||||
-- 机密客户端(如后端服务)必须验证 secret
|
return
|
||||||
if stored_secret ~= "public" and stored_secret ~= client_secret then
|
|
||||||
return ngx.say(cjson.encode({
|
|
||||||
error = "invalid_client",
|
|
||||||
error_description = "client_secret invalid"
|
|
||||||
}))
|
|
||||||
end
|
end
|
||||||
|
-- 验证成功删除
|
||||||
|
shared_dict:delete(code_key)
|
||||||
|
|
||||||
-- 4.3 验证 Refresh Token 签名(假设 Refresh Token 是 JWT 格式)
|
-- 6. 生成密钥对
|
||||||
local refresh_jwt = jwt:verify(jwt_secret, refresh_token)
|
local pub_key, priv_key, err = rsa.generate_rsa_keys(2048)
|
||||||
if not refresh_jwt.valid then
|
if err then
|
||||||
return ngx.say(cjson.encode({
|
--print("密钥生成失败: ", err)
|
||||||
error = "invalid_grant",
|
local result = resp:json(0x00001)
|
||||||
error_description = "refresh_token invalid: " .. (refresh_jwt.reason or "unknown")
|
resp:send(result)
|
||||||
}))
|
return
|
||||||
end
|
end
|
||||||
|
print("token pubkey:", pub_key)
|
||||||
-- 4.4 校验 Token 绑定关系(client_id 必须与 JWT 中一致)
|
local user_id = jsonData.user_id
|
||||||
if refresh_jwt.payload.client_id ~= client_id then
|
--print("token user_id:", user_id)
|
||||||
return ngx.say(cjson.encode({
|
local client_id = jsonData.client_id
|
||||||
error = "invalid_grant",
|
--print("token client_id:", client_id)
|
||||||
error_description = "refresh_token not bound to client_id"
|
local scope = jsonData.scope
|
||||||
}))
|
--print("token scope:", scope)
|
||||||
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 access_token_ttl = 10 * 60 --十分钟
|
||||||
local refresh_token_ttl = 7 * 24 * 3600 --7天
|
local refresh_token_ttl = 7 * 24 * 3600 --7天
|
||||||
-- 5.2 生成新 Access Token
|
-- 7 生成新 Access Token
|
||||||
local access_payload = {
|
local access_payload = {
|
||||||
sub = refresh_jwt.payload.sub, -- 用户ID
|
sub = user_id, -- 用户ID
|
||||||
client_id = client_id,
|
client_id = client_id,
|
||||||
scope = refresh_jwt.payload.scope or "",
|
scope = scope or "",
|
||||||
exp = ngx.time() + access_token_ttl,
|
exp = ngx.time() + access_token_ttl,
|
||||||
jti = ngx.md5(ngx.time() .. math.random() .. client_id) -- 唯一标识
|
jti = ngx.md5(ngx.time() .. math.random() .. client_id) -- 唯一标识
|
||||||
}
|
}
|
||||||
local new_access_token = jwt:sign(jwt_secret, {
|
local new_access_token = jwt:sign(priv_key, {
|
||||||
header = { typ = "JWT", alg = "HS256" },
|
header = { typ = "JWT", alg = "HS256" },
|
||||||
payload = access_payload
|
payload = access_payload
|
||||||
})
|
})
|
||||||
|
|
||||||
-- 5.3 生成新 Refresh Token(滚动刷新)
|
-- 8 生成新 Refresh Token(滚动刷新)
|
||||||
local refresh_payload = {
|
local refresh_payload = {
|
||||||
sub = refresh_jwt.payload.sub,
|
sub = user_id,
|
||||||
client_id = client_id,
|
client_id = client_id,
|
||||||
scope = refresh_jwt.payload.scope or "",
|
scope = scope or "",
|
||||||
exp = ngx.time() + refresh_token_ttl,
|
exp = ngx.time() + refresh_token_ttl,
|
||||||
jti = ngx.md5(ngx.time() .. math.random() * 1000 .. client_id)
|
jti = ngx.md5(ngx.time() .. math.random() * 1000 .. client_id)
|
||||||
}
|
}
|
||||||
local new_refresh_token = jwt:sign(jwt_secret, {
|
local new_refresh_token = jwt:sign(priv_key, {
|
||||||
header = { typ = "JWT", alg = "HS256" },
|
header = { typ = "JWT", alg = "HS256" },
|
||||||
payload = refresh_payload
|
payload = refresh_payload
|
||||||
})
|
})
|
||||||
|
|
||||||
-- 6. 返回结果
|
-- 9、生存id_token
|
||||||
ngx.say(cjson.encode({
|
-- 创建JWT的payload
|
||||||
access_token = new_access_token,
|
local payload = {
|
||||||
token_type = "Bearer",
|
iss = request_uri,
|
||||||
expires_in = access_token_ttl,
|
sub = user_id,
|
||||||
refresh_token = new_refresh_token,
|
name = user_id,
|
||||||
refresh_expires_in = refresh_token_ttl,
|
iat = os.time(),
|
||||||
scope = access_payload.scope,
|
exp = os.time() + 3600
|
||||||
issued_at = ngx.time(),
|
}
|
||||||
jti = access_payload.jti
|
|
||||||
}))
|
-- 使用私钥生成JWT
|
||||||
|
local jwt_obj = jwt:sign(priv_key, {
|
||||||
|
header = {
|
||||||
|
type = "JWT",
|
||||||
|
alg = "RS256"
|
||||||
|
},
|
||||||
|
payload = payload
|
||||||
|
})
|
||||||
|
if not jwt_obj then
|
||||||
|
local result = resp:json(0x00001)
|
||||||
|
resp:send(result)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
--ngx.say("Generated JWT: ", jwt_obj)
|
||||||
|
-- 10. 返回结果
|
||||||
|
local ret = {}
|
||||||
|
ret.access_token = new_access_token
|
||||||
|
ret.token_type = "Bearer"
|
||||||
|
ret.expires_in = access_token_ttl
|
||||||
|
ret.refresh_token = new_refresh_token
|
||||||
|
ret.id_token = jwt_obj
|
||||||
|
local result = resp:json(ngx.HTTP_OK, ret)
|
||||||
|
resp:send(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
--用户进行登陆然后验证返回code
|
--用户进行登陆然后验证返回code
|
||||||
|
|
@ -247,20 +247,19 @@ function _M:login()
|
||||||
--验证json数据是否正确
|
--验证json数据是否正确
|
||||||
local ok, data = pcall(cjson.decode, body_data)
|
local ok, data = pcall(cjson.decode, body_data)
|
||||||
if not ok then
|
if not ok then
|
||||||
print("JSON解析失败:", data)
|
|
||||||
local result = resp:json(0x000001)
|
local result = resp:json(0x000001)
|
||||||
resp:send(result)
|
resp:send(result)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- 验证数据是否符合json
|
-- 验证数据是否符合json
|
||||||
local valid, errors = validator.validatorLogin(data)
|
local valid, errors = validator.validateLogin(data)
|
||||||
--验证失败则返回
|
--验证失败则返回
|
||||||
if not valid then
|
if not valid then
|
||||||
local result = resp:json(0x000001)
|
local result = resp:json(0x000001)
|
||||||
resp:send(result)
|
resp:send(result)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local code, ret = loginDao.login(data)
|
local code, ret = oauthDao.login(data)
|
||||||
--读取数据错误
|
--读取数据错误
|
||||||
if code ~= 0 or table.getn(ret) < 0 then
|
if code ~= 0 or table.getn(ret) < 0 then
|
||||||
local result = resp:json(0x000001)
|
local result = resp:json(0x000001)
|
||||||
|
|
@ -287,18 +286,62 @@ end
|
||||||
|
|
||||||
--根据Access-Token获取相应用户的账户信息
|
--根据Access-Token获取相应用户的账户信息
|
||||||
function _M:userinfo()
|
function _M:userinfo()
|
||||||
--读取请求体的数据
|
--获取用户认证数据信息
|
||||||
ngx.req.read_body()
|
local auth_header = ngx.var.http_Authorization
|
||||||
--获取请求数据
|
|
||||||
local body_data = ngx.req.get_body_data()
|
--如果请求头中没有令牌,则直接返回401
|
||||||
-- 验证数据是否符合json
|
if auth_header == nil or auth_header == "" then
|
||||||
local ok = validator.validatorUserinfo(body_data)
|
ngx.log(ngx.WARN, "没有找到令牌数据")
|
||||||
--验证失败则返回
|
ngx.status = ngx.HTTP_UNAUTHORIZED
|
||||||
|
ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||||
|
end
|
||||||
|
|
||||||
|
--查找令牌中的Bearer前缀字符
|
||||||
|
local data = {}
|
||||||
|
data.Authorization = auth_header
|
||||||
|
local ok = validator.validateUserinfo(data)
|
||||||
if not ok then
|
if not ok then
|
||||||
local result = resp:json(0x000001)
|
ngx.log(ngx.WARN, "令牌格式不正确")
|
||||||
|
ngx.status = ngx.HTTP_UNAUTHORIZED
|
||||||
|
ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||||
|
end
|
||||||
|
--获取token的数据值
|
||||||
|
local token = string.sub(auth_header,8)
|
||||||
|
--校验令牌
|
||||||
|
local pub_key, priv_key, err = rsa.generate_rsa_keys(2048)
|
||||||
|
if err then
|
||||||
|
--print("密钥生成失败: ", err)
|
||||||
|
local result = resp:json(0x00001)
|
||||||
resp:send(result)
|
resp:send(result)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
print("userinfo pubkey:", pub_key)
|
||||||
|
local jwt_obj = jwt:verify(pub_key, token)
|
||||||
|
--如果校验结果中的verified==false,则表示令牌无效
|
||||||
|
if jwt_obj.verified == false then
|
||||||
|
ngx.log(ngx.WARN, "Invalid token: ".. jwt_obj.reason)
|
||||||
|
ngx.status = ngx.HTTP_UNAUTHORIZED
|
||||||
|
ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||||
|
end
|
||||||
|
|
||||||
|
--判断token是否超时 --令牌已过期
|
||||||
|
if jwt_obj.payload.exp and ngx.time() > jwt_obj.payload.exp then
|
||||||
|
ngx.log(ngx.WARN, "token timeout ".. jwt_obj.reason)
|
||||||
|
ngx.status = ngx.HTTP_UNAUTHORIZED
|
||||||
|
ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||||
|
end
|
||||||
|
--获取token中的信息进行所需用户的信息返回
|
||||||
|
|
||||||
|
--
|
||||||
|
local ret = {}
|
||||||
|
ret.sub = 248289761001
|
||||||
|
ret.name = "Jane Doe"
|
||||||
|
ret.given_name = "Jane"
|
||||||
|
ret.family_name = "Doe"
|
||||||
|
ret.preferred_username = "j.doe"
|
||||||
|
ret.email = "janedoe@example.com"
|
||||||
|
local result = resp:json(ngx.HTTP_OK, ret)
|
||||||
|
resp:send(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
--回收Access-Token
|
--回收Access-Token
|
||||||
|
|
@ -307,8 +350,15 @@ function _M:logout()
|
||||||
ngx.req.read_body()
|
ngx.req.read_body()
|
||||||
--获取请求数据
|
--获取请求数据
|
||||||
local body_data = ngx.req.get_body_data()
|
local body_data = ngx.req.get_body_data()
|
||||||
|
--验证json数据是否正确
|
||||||
|
local ok, data = pcall(cjson.decode, body_data)
|
||||||
|
if not ok then
|
||||||
|
local result = resp:json(0x000001)
|
||||||
|
resp:send(result)
|
||||||
|
return
|
||||||
|
end
|
||||||
-- 验证数据是否符合json
|
-- 验证数据是否符合json
|
||||||
local ok = validator.validatorLogout(body_data)
|
local ok = validator.validateLogout(data)
|
||||||
--验证失败则返回
|
--验证失败则返回
|
||||||
if not ok then
|
if not ok then
|
||||||
local result = resp:json(0x000001)
|
local result = resp:json(0x000001)
|
||||||
|
|
@ -337,8 +387,15 @@ function _M:refresh()
|
||||||
ngx.req.read_body()
|
ngx.req.read_body()
|
||||||
--获取请求数据
|
--获取请求数据
|
||||||
local body_data = ngx.req.get_body_data()
|
local body_data = ngx.req.get_body_data()
|
||||||
|
--验证json数据是否正确
|
||||||
|
local ok, data = pcall(cjson.decode, body_data)
|
||||||
|
if not ok then
|
||||||
|
local result = resp:json(0x000001)
|
||||||
|
resp:send(result)
|
||||||
|
return
|
||||||
|
end
|
||||||
-- 验证数据是否符合json
|
-- 验证数据是否符合json
|
||||||
local ok = validator.validatorRefresh(body_data)
|
local ok = validator.validateRefresh(data)
|
||||||
--验证失败则返回
|
--验证失败则返回
|
||||||
if not ok then
|
if not ok then
|
||||||
local result = resp:json(0x000001)
|
local result = resp:json(0x000001)
|
||||||
|
|
@ -353,8 +410,15 @@ function _M:checklogin()
|
||||||
ngx.req.read_body()
|
ngx.req.read_body()
|
||||||
--获取请求数据
|
--获取请求数据
|
||||||
local body_data = ngx.req.get_body_data()
|
local body_data = ngx.req.get_body_data()
|
||||||
|
--验证json数据是否正确
|
||||||
|
local ok, data = pcall(cjson.decode, body_data)
|
||||||
|
if not ok then
|
||||||
|
local result = resp:json(0x000001)
|
||||||
|
resp:send(result)
|
||||||
|
return
|
||||||
|
end
|
||||||
-- 验证数据是否符合json
|
-- 验证数据是否符合json
|
||||||
local ok = validator.validatorLogout(body_data)
|
local ok = validator.validateChecklogin(data)
|
||||||
--验证失败则返回
|
--验证失败则返回
|
||||||
if not ok then
|
if not ok then
|
||||||
local result = resp:json(0x000001)
|
local result = resp:json(0x000001)
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,16 @@ local schema = {
|
||||||
required = { "username", "password" } -- 必填字段
|
required = { "username", "password" } -- 必填字段
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local schemaToken = {
|
||||||
|
type = "object",
|
||||||
|
properties = {
|
||||||
|
grant_type = { type = "string" },
|
||||||
|
code = { type = "string" },
|
||||||
|
redirect_uri = { type = "string" },
|
||||||
|
},
|
||||||
|
required = { "grant_type", "code", "redirect_uri" }
|
||||||
|
}
|
||||||
|
|
||||||
-- 原始JSON字符串
|
-- 原始JSON字符串
|
||||||
--local jsonStr = [[
|
--local jsonStr = [[
|
||||||
--{
|
--{
|
||||||
|
|
@ -92,6 +102,7 @@ local schema = {
|
||||||
--}
|
--}
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
|
--[[
|
||||||
ngx.req.read_body()
|
ngx.req.read_body()
|
||||||
--获取请求数据
|
--获取请求数据
|
||||||
local jsonStr = ngx.req.get_body_data()
|
local jsonStr = ngx.req.get_body_data()
|
||||||
|
|
@ -108,7 +119,7 @@ end
|
||||||
|
|
||||||
-- 验证函数
|
-- 验证函数
|
||||||
local function validateJson(data)
|
local function validateJson(data)
|
||||||
local validator = jsonschema.generate_validator(schema)
|
local validator = jsonschema.generate_validator(schemaToken)
|
||||||
local valid, errors = validator(data) -- 注意返回两个值
|
local valid, errors = validator(data) -- 注意返回两个值
|
||||||
|
|
||||||
if not valid then
|
if not valid then
|
||||||
|
|
@ -127,6 +138,7 @@ else
|
||||||
end
|
end
|
||||||
|
|
||||||
do return end
|
do return end
|
||||||
|
--]]
|
||||||
|
|
||||||
-- 生成RSA密钥对
|
-- 生成RSA密钥对
|
||||||
local function generate_rsa_keys(length)
|
local function generate_rsa_keys(length)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ local schemaAuth = {
|
||||||
}
|
}
|
||||||
|
|
||||||
--获取授权码
|
--获取授权码
|
||||||
function _M:validatorAuthorize(jsonData)
|
function _M.validateAuthorize(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaAuth)
|
local validator = jsonschema.generate_validator(schemaAuth)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
@ -39,7 +39,7 @@ local schemaToken = {
|
||||||
}
|
}
|
||||||
|
|
||||||
--根据授权码获取Access-Token
|
--根据授权码获取Access-Token
|
||||||
function _M:validatorToken(jsonData)
|
function _M.validateToken(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaToken)
|
local validator = jsonschema.generate_validator(schemaToken)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
@ -58,7 +58,7 @@ local schemaLogin = {
|
||||||
}
|
}
|
||||||
|
|
||||||
--回收Access-Token
|
--回收Access-Token
|
||||||
function _M:validatorLogin(jsonData)
|
function _M.validateLogin(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaLogin)
|
local validator = jsonschema.generate_validator(schemaLogin)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
@ -66,15 +66,14 @@ function _M:validatorLogin(jsonData)
|
||||||
end
|
end
|
||||||
|
|
||||||
local schemaUserInfo = {
|
local schemaUserInfo = {
|
||||||
type = "object",
|
type = 'object',
|
||||||
properties = {
|
properties = {
|
||||||
Authorization = { type = "string" },
|
Authorization = {type = 'string', minLength = 8, pattern = 'Bearer\\s+(.+)$'},
|
||||||
},
|
}, required = {"Authorization"}
|
||||||
required = { "Authorization" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--根据Access-Token获取相应用户的账户信息
|
--根据Access-Token获取相应用户的账户信息
|
||||||
function _M:validatorUserinfo(jsonData)
|
function _M.validateUserinfo(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaUserInfo)
|
local validator = jsonschema.generate_validator(schemaUserInfo)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
@ -90,7 +89,7 @@ local schemaLogout = {
|
||||||
}
|
}
|
||||||
|
|
||||||
--回收Access-Token
|
--回收Access-Token
|
||||||
function _M:validatorLogout(jsonData)
|
function _M.validateLogout(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaLogout)
|
local validator = jsonschema.generate_validator(schemaLogout)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
@ -106,7 +105,7 @@ local schemaRefresh = {
|
||||||
}
|
}
|
||||||
|
|
||||||
--根据Refresh-Token刷新Access-Token
|
--根据Refresh-Token刷新Access-Token
|
||||||
function _M:validatorRefresh(jsonData)
|
function _M.validateRefresh(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaRefresh)
|
local validator = jsonschema.generate_validator(schemaRefresh)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
@ -122,7 +121,7 @@ local schemaChecklogin = {
|
||||||
}
|
}
|
||||||
|
|
||||||
--验证token是否有效
|
--验证token是否有效
|
||||||
function _M:validatorChecklogin(jsonData)
|
function _M.validateChecklogin(jsonData)
|
||||||
-- 验证数据是否符合schema
|
-- 验证数据是否符合schema
|
||||||
local validator = jsonschema.generate_validator(schemaChecklogin)
|
local validator = jsonschema.generate_validator(schemaChecklogin)
|
||||||
local result = validator(jsonData)
|
local result = validator(jsonData)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user