From 03658c90042a5edae01cdceba0d8a17baa8be868 Mon Sep 17 00:00:00 2001 From: wanglei <34475144@qq.com> Date: Thu, 13 Nov 2025 22:30:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9oauth=E4=B8=ADauthorize?= =?UTF-8?q?=E5=92=8Ctoken=E5=87=BD=E6=95=B0=E4=B8=9A=E5=8A=A1=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E5=B9=B6=E5=A2=9E=E5=8A=A0=E7=94=9F=E6=88=90?= =?UTF-8?q?code=E5=92=8Ccode=E8=AE=A4=E8=AF=81=E7=9B=B8=E5=85=B3=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/service/oauth/oauth.lua | 177 ++++------------------------------ src/util/authcode.lua | 57 +++++++++++ src/validator/oauth/oauth.lua | 51 ---------- 3 files changed, 74 insertions(+), 211 deletions(-) create mode 100644 src/util/authcode.lua diff --git a/src/service/oauth/oauth.lua b/src/service/oauth/oauth.lua index 7a91841..4da0608 100644 --- a/src/service/oauth/oauth.lua +++ b/src/service/oauth/oauth.lua @@ -7,9 +7,9 @@ local resp = require("util.response") local oauthDao = 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 rsa = require("util.rsa") +local authcode = require("util.authcode") local _M = {} @@ -61,29 +61,14 @@ function _M:authorize() resp:send(result) return end - -- 4. 生成授权码(随机字符串,确保唯一性) - local function generate_code() - local str = require "resty.string" - local random = require "resty.random" - local bytes = random.bytes(16) - return str.to_hex(bytes) + -- 4. 生成授权码(随机字符串,确保唯一性)(用户ID、客户端ID、scope、生成时间) + local auth_code, err = authcode.create("123456", args.client_id, ngx.var.request_uri, args.scope) + if not auth_code then + ngx.log(ngx.ERR, "生成授权码失败: ", err) + ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) 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) + -- 5. 重定向到客户端回调地址,携带授权码和原始 state(防 CSRF) local redirect_url = args.redirect_uri .. "?code=" .. code .. "&state=" .. args.state local rest = {} rest.redirect_uri = args.redirect_uri @@ -121,31 +106,19 @@ function _M:token() -- 2. 校验必填参数验证数据是否符合json local ok = validator.validateToken(args) - --验证失败则返回 if not ok then local result = resp:json(0x000001) resp:send(result) return end - - -- 4. 校验 code 有效性 - local code = args.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 - -- code 超出时效,需要重新获取code - --local result = resp:json(0x000001) - --resp:send(result) - 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 + -- 3. 校验 code 有效性 + local code_data, err = authcode.consume(args.code, args.client_id) + if not code_data then + ngx.log(ngx.ERR, "授权码验证失败: ", err) + ngx.exit(ngx.HTTP_BAD_REQUEST) end - --print("token code_data:", code_data) - local jsonData = cjson.decode(code_data) - -- 5、验证redirect_url地址的正确性 - local request_uri = jsonData.redirect_uri + -- 4、验证redirect_url地址的正确性 + local request_uri = code_data.redirect_uri print("token request_uri:", request_uri) if request_uri ~= args.redirect_url then --print("token redirect_url:", request_uri, args.redirect_url) @@ -154,9 +127,6 @@ function _M:token() resp:send(result) return end - -- 验证成功删除 - shared_dict:delete(code_key) - -- 6. 生成密钥对 local pub_key, priv_key, err = rsa.generate_rsa_keys(2048) if err then @@ -166,11 +136,11 @@ function _M:token() return end print("token pubkey:", pub_key) - local user_id = jsonData.user_id + local user_id = code_data.user_id --print("token user_id:", user_id) - local client_id = jsonData.client_id + local client_id = code_data.client_id --print("token client_id:", client_id) - local scope = jsonData.scope + local scope = code_data.scope --print("token scope:", scope) local access_token_ttl = 10 * 60 --十分钟 local refresh_token_ttl = 7 * 24 * 3600 --7天 @@ -209,7 +179,6 @@ function _M:token() iat = os.time(), exp = os.time() + 3600 } - -- 使用私钥生成JWT local jwt_obj = jwt:sign(priv_key, { header = { @@ -235,55 +204,6 @@ function _M:token() resp:send(result) end ---用户进行登陆然后验证返回code -function _M:login() - --获取远端客户端的IP地址 - local client_ip = ngx.var.remote_addr - ngx.log(ngx.INFO, "client_ip:"..client_ip.." oauth login system") - --读取请求体的数据 - 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 valid, errors = validator.validateLogin(data) - --验证失败则返回 - if not valid then - local result = resp:json(0x000001) - resp:send(result) - return - end - local code, ret = oauthDao.login(data) - --读取数据错误 - if code ~= 0 or table.getn(ret) < 0 then - local result = resp:json(0x000001) - resp:send(result) - return - end - - -- 用户验证成功后,返回code值:用户id当前时间和随机数进行md5后生存一个code - local user_id = ret[1].id - local username = ret[1].username - local current_time = ngx.time() - local code = ngx.md5(user_id..current_time..math.random()) - - -- 将code放入到共享内存中 - local key = code -- 作为键值 - local shared_dict = ngx.shared.codeDict - shared_dict:set(key, user_id) - -- 数据时效性先设置5分钟 - shared_dict:expire(key, 5 * 60) - --发送code到前端请求 - local result = resp:json(0, code) - resp:send(result) -end - --根据Access-Token获取相应用户的账户信息 function _M:userinfo() --获取用户认证数据信息 @@ -295,7 +215,6 @@ function _M:userinfo() ngx.status = ngx.HTTP_UNAUTHORIZED ngx.exit(ngx.HTTP_UNAUTHORIZED) end - --查找令牌中的Bearer前缀字符 local data = {} data.Authorization = auth_header @@ -331,8 +250,6 @@ function _M:userinfo() ngx.exit(ngx.HTTP_UNAUTHORIZED) end --获取token中的信息进行所需用户的信息返回 - - -- local ret = {} ret.sub = 248289761001 ret.name = "Jane Doe" @@ -344,43 +261,6 @@ function _M:userinfo() resp:send(result) end ---回收Access-Token -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.validateLogout(data) - --验证失败则返回 - if not ok then - local result = resp:json(0x000001) - resp:send(result) - return - end - - --用户验证成功后,返回code值:用户id当前时间和随机数进行md5后生存一个code - local user_id = "11" - local current_time = ngx.time() - local code = ngx.md5(user_id..current_time..math.random()) - - --将code放入到共享内存中 - local key = user_id.."-code" - local shared_dict = ngx.shared.codeDict - shared_dict:set(key, code) - shared_dict:expire(key, 10) - --发送code到前端请求 - local result = resp:json(0, code) - resp:send(result) -end - --根据Refresh-Token刷新Access-Token function _M:refresh() --读取请求体的数据 @@ -404,27 +284,4 @@ function _M:refresh() end end ---验证token是否有效 -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.validateChecklogin(data) - --验证失败则返回 - if not ok then - local result = resp:json(0x000001) - resp:send(result) - return - end -end - return _M \ No newline at end of file diff --git a/src/util/authcode.lua b/src/util/authcode.lua new file mode 100644 index 0000000..2da9777 --- /dev/null +++ b/src/util/authcode.lua @@ -0,0 +1,57 @@ +--- +--- Generated by EmmyLua(https://github.com/EmmyLua) +--- Created by admin. +--- DateTime: 2025/11/13 22:08 +--- 授权码生成和认证 + +local str = require "resty.string" +local random = require "resty.random" + +local _M = {} + +-- 生成随机授权码(20字节) +local function generate_code() + local random_bytes = random.bytes(20, true) + return str.to_hex(random_bytes) +end + +-- 存储授权码(有效期5分钟) +function _M.create(user_id, client_id, redirect_uri, scope) + local code = generate_code() + local code_key = "auth_code-"..code + local code_data = cjson.encode({ + user_id = user_id, + client_id = client_id, + redirect_uri = redirect_uri, + scope = scope, + expires_at = ngx.time() + 300 -- 5分钟过期 + }) + local shared_dict = ngx.shared.codeDict + shared_dict:set(code_key, code_data) + shared_dict:expire(code_key, 300) --时效性为5分钟 + return code +end + +-- 验证并消费授权码(一次性有效) +function _M.consume(code, client_id) + local code_key = "auth_code-"..code + local shared_dict = ngx.shared.codeDict + local data = shared_dict:get(code_key) + if data == nil then + return nil, "无效的授权码" + end + + -- 消费后立即删除(一次性) + shared_dict:delete(code_key) + + local code_data = cjson.decode(data) + if code_data.client_id ~= client_id then + return nil, "客户端不匹配" + end + if code_data.expires_at < ngx.time() then + return nil, "授权码已过期" + end + return code_data +end + +return _M \ No newline at end of file diff --git a/src/validator/oauth/oauth.lua b/src/validator/oauth/oauth.lua index a1c3450..b9e2efd 100644 --- a/src/validator/oauth/oauth.lua +++ b/src/validator/oauth/oauth.lua @@ -46,25 +46,6 @@ function _M.validateToken(jsonData) return result end -local schemaLogin = { - type = "object", - properties = { - username = { type = "string" }, - password = { type = "string" }, - captcha = { type = "string" }, - checkKey = { type = "string" }, - }, - required = { "username", "password" } -} - ---回收Access-Token -function _M.validateLogin(jsonData) - -- 验证数据是否符合schema - local validator = jsonschema.generate_validator(schemaLogin) - local result = validator(jsonData) - return result -end - local schemaUserInfo = { type = 'object', properties = { @@ -80,22 +61,6 @@ function _M.validateUserinfo(jsonData) return result end -local schemaLogout = { - type = "object", - properties = { - Authorization = { type = "string" }, - }, - required = { "Authorization" } -} - ---回收Access-Token -function _M.validateLogout(jsonData) - -- 验证数据是否符合schema - local validator = jsonschema.generate_validator(schemaLogout) - local result = validator(jsonData) - return result -end - local schemaRefresh = { type = "object", properties = { @@ -112,20 +77,4 @@ function _M.validateRefresh(jsonData) return result end -local schemaChecklogin = { - type = "object", - properties = { - Authorization = { type = "string" }, - }, - required = { "Authorization" } -} - ---验证token是否有效 -function _M.validateChecklogin(jsonData) - -- 验证数据是否符合schema - local validator = jsonschema.generate_validator(schemaChecklogin) - local result = validator(jsonData) - return result -end - return _M \ No newline at end of file