From 2cbc0e3d3f94f3484775f98bd64ed8aabb3687a7 Mon Sep 17 00:00:00 2001 From: wanglei <34475144@qqcom> Date: Thu, 13 Nov 2025 22:00:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BA=94=E7=94=A8=E7=A8=8B?= =?UTF-8?q?=E5=BA=8Fop=E7=AB=AF=E7=82=B9=EF=BC=8C=E5=A2=9E=E5=8A=A0openidc?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E7=AB=AF=E5=8F=A3=EF=BC=8C=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E4=B8=BA=E6=B5=8B=E8=AF=95=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/nginx.conf | 21 ++++- src/service/oauth/oauth.lua | 154 ++++++++++++++++++++++++++-------- src/test/test.lua | 12 ++- src/validator/oauth/oauth.lua | 21 +++-- 4 files changed, 161 insertions(+), 47 deletions(-) diff --git a/conf/nginx.conf b/conf/nginx.conf index e5e9c17..494de9f 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -26,7 +26,7 @@ http { #在Nginx启动时执行的Lua代码块 #oauth2.0第三方验证后将code放到共享内存中 - lua_shared_dict codeDict 5m; + lua_shared_dict codeDict 10m; #init_by_lua_block { # -- 定义一个全局变量 # ngx.log(ngx.INFO, "Initializing global variable") @@ -65,6 +65,25 @@ http { 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'; diff --git a/src/service/oauth/oauth.lua b/src/service/oauth/oauth.lua index 82088ec..7a91841 100644 --- a/src/service/oauth/oauth.lua +++ b/src/service/oauth/oauth.lua @@ -17,9 +17,9 @@ local _M = {} function _M:authorize() local args = ngx.req.get_uri_args() if ngx.req.get_method() == "POST" then - --读取请求体的数据 + -- 读取请求体的数据 ngx.req.read_body() - --获取请求数据 + -- 获取请求数据 local body_data = ngx.req.get_body_data() -- 验证json数据是否正确 local ok, data = pcall(cjson.decode, body_data) @@ -27,7 +27,7 @@ function _M:authorize() return ngx.exit(ngx.HTTP_BAD_REQUEST) end -- 校验客户端请求参数 - ok = validator.validatorAuthorize(data) + ok = validator.validateAuthorize(data) --验证失败则返回 if not ok then return ngx.exit(ngx.HTTP_BAD_REQUEST) @@ -69,9 +69,9 @@ function _M:authorize() return str.to_hex(bytes) end local code = generate_code() - print("authorize generate_code:", code) + --print("authorize generate_code:", code) -- 5、存储授权码信息(用户ID、客户端ID、scope、生成时间) - local code_key = "auth_code:" .. code + local code_key = "auth_code-"..code local code_data = cjson.encode({ user_id = "123456", client_id = args.client_id, @@ -80,11 +80,16 @@ function _M:authorize() created_at = ngx.time() }) local shared_dict = ngx.shared.codeDict - shared_dict:set(code_key, code_data, 5 * 60) - + 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 result = resp:json(ngx.HTTP_OK, redirect_url) + 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 @@ -94,22 +99,30 @@ function _M:token() -- 1. 解析请求参数(支持 form-data 和 json) local content_type = ngx.req.get_headers()["Content-Type"] or "" local args = {} + --print("token content_type:", content_type) 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) 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 -- 默认解析 form-urlencoded args = ngx.req.get_post_args() end - print("args:", args) + -- 2. 校验必填参数验证数据是否符合json - local ok = validator.validatorToken(args) + local ok = validator.validateToken(args) --验证失败则返回 if not ok then - print("validatorToken failed") local result = resp:json(0x000001) resp:send(result) return @@ -117,10 +130,10 @@ function _M:token() -- 4. 校验 code 有效性 local code = args.code - local code_key = "auth_code:" .. code + local code_key = "auth_code-"..code local shared_dict = ngx.shared.codeDict local code_data = shared_dict:get(code_key) - if code_data ~= nil then + if code_data == nil then -- code 超出时效,需要重新获取code --local result = resp:json(0x000001) --resp:send(result) @@ -129,12 +142,17 @@ function _M:token() resp:send(result) return end + --print("token code_data:", code_data) + local jsonData = cjson.decode(code_data) -- 5、验证redirect_url地址的正确性 - local request_uri = code_data["request_uri"] + local request_uri = jsonData.redirect_uri + print("token request_uri:", request_uri) if request_uri ~= args.redirect_url then + --print("token redirect_url:", request_uri, args.redirect_url) local login_url = "/login?redirect=" .. ngx.escape_uri(request_uri) local result = resp:json(ngx.HTTP_MOVED_TEMPORARILY, login_url) resp:send(result) + return end -- 验证成功删除 shared_dict:delete(code_key) @@ -142,14 +160,18 @@ function _M:token() -- 6. 生成密钥对 local pub_key, priv_key, err = rsa.generate_rsa_keys(2048) if err then - print("密钥生成失败: ", err) + --print("密钥生成失败: ", err) local result = resp:json(0x00001) resp:send(result) return end - local user_id = code_data["user_id"] - local client_id = code_data["client_id"] - local scope = code_data["scope"] + print("token pubkey:", pub_key) + local user_id = jsonData.user_id + --print("token user_id:", user_id) + local client_id = jsonData.client_id + --print("token client_id:", client_id) + local scope = jsonData.scope + --print("token scope:", scope) local access_token_ttl = 10 * 60 --十分钟 local refresh_token_ttl = 7 * 24 * 3600 --7天 -- 7 生成新 Access Token @@ -225,13 +247,12 @@ function _M:login() --验证json数据是否正确 local ok, data = pcall(cjson.decode, body_data) if not ok then - print("JSON解析失败:", data) local result = resp:json(0x000001) resp:send(result) return end -- 验证数据是否符合json - local valid, errors = validator.validatorLogin(data) + local valid, errors = validator.validateLogin(data) --验证失败则返回 if not valid then local result = resp:json(0x000001) @@ -265,18 +286,62 @@ end --根据Access-Token获取相应用户的账户信息 function _M:userinfo() - --读取请求体的数据 - ngx.req.read_body() - --获取请求数据 - local body_data = ngx.req.get_body_data() - -- 验证数据是否符合json - local ok = validator.validatorUserinfo(body_data) - --验证失败则返回 + --获取用户认证数据信息 + local auth_header = ngx.var.http_Authorization + + --如果请求头中没有令牌,则直接返回401 + if auth_header == nil or auth_header == "" then + 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 - 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) return 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 --回收Access-Token @@ -285,8 +350,15 @@ function _M:logout() ngx.req.read_body() --获取请求数据 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 - local ok = validator.validatorLogout(body_data) + local ok = validator.validateLogout(data) --验证失败则返回 if not ok then local result = resp:json(0x000001) @@ -315,8 +387,15 @@ function _M:refresh() ngx.req.read_body() --获取请求数据 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 - local ok = validator.validatorRefresh(body_data) + local ok = validator.validateRefresh(data) --验证失败则返回 if not ok then local result = resp:json(0x000001) @@ -331,8 +410,15 @@ function _M:checklogin() ngx.req.read_body() --获取请求数据 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 - local ok = validator.validatorLogout(body_data) + local ok = validator.validateChecklogin(data) --验证失败则返回 if not ok then local result = resp:json(0x000001) diff --git a/src/test/test.lua b/src/test/test.lua index d22ea86..1a1c493 100644 --- a/src/test/test.lua +++ b/src/test/test.lua @@ -82,6 +82,16 @@ local schema = { 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字符串 --local jsonStr = [[ --{ @@ -109,7 +119,7 @@ end -- 验证函数 local function validateJson(data) - local validator = jsonschema.generate_validator(schema) + local validator = jsonschema.generate_validator(schemaToken) local valid, errors = validator(data) -- 注意返回两个值 if not valid then diff --git a/src/validator/oauth/oauth.lua b/src/validator/oauth/oauth.lua index bb27839..a1c3450 100644 --- a/src/validator/oauth/oauth.lua +++ b/src/validator/oauth/oauth.lua @@ -21,7 +21,7 @@ local schemaAuth = { } --获取授权码 -function _M:validatorAuthorize(jsonData) +function _M.validateAuthorize(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaAuth) local result = validator(jsonData) @@ -39,7 +39,7 @@ local schemaToken = { } --根据授权码获取Access-Token -function _M:validatorToken(jsonData) +function _M.validateToken(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaToken) local result = validator(jsonData) @@ -58,7 +58,7 @@ local schemaLogin = { } --回收Access-Token -function _M:validatorLogin(jsonData) +function _M.validateLogin(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaLogin) local result = validator(jsonData) @@ -66,15 +66,14 @@ function _M:validatorLogin(jsonData) end local schemaUserInfo = { - type = "object", + type = 'object', properties = { - Authorization = { type = "string" }, - }, - required = { "Authorization" } + Authorization = {type = 'string', minLength = 8, pattern = 'Bearer\\s+(.+)$'}, + }, required = {"Authorization"} } --根据Access-Token获取相应用户的账户信息 -function _M:validatorUserinfo(jsonData) +function _M.validateUserinfo(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaUserInfo) local result = validator(jsonData) @@ -90,7 +89,7 @@ local schemaLogout = { } --回收Access-Token -function _M:validatorLogout(jsonData) +function _M.validateLogout(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaLogout) local result = validator(jsonData) @@ -106,7 +105,7 @@ local schemaRefresh = { } --根据Refresh-Token刷新Access-Token -function _M:validatorRefresh(jsonData) +function _M.validateRefresh(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaRefresh) local result = validator(jsonData) @@ -122,7 +121,7 @@ local schemaChecklogin = { } --验证token是否有效 -function _M:validatorChecklogin(jsonData) +function _M.validateChecklogin(jsonData) -- 验证数据是否符合schema local validator = jsonschema.generate_validator(schemaChecklogin) local result = validator(jsonData)