diff --git a/src/dao/oauth/oauth.lua b/src/dao/oauth/oauth.lua index 6c63b13..f69ff0c 100644 --- a/src/dao/oauth/oauth.lua +++ b/src/dao/oauth/oauth.lua @@ -4,22 +4,10 @@ --- DateTime: 2025/10/29 23:36 --- local userDao = require("dao.system.user") +local applicationDao = require("dao.system.application") local _M = {} ---认证用户返回用户数据信息 -local function authenticate(name, passwd) - --验证用户名是否为空 - if name == "" then - return 0x010003, nil - end - --验证密码是否为空 - if passwd == "" then - return 0x010002, nil - end - return userDao:adjustUser(name, passwd) -end - --用户登录业务逻辑处理 function _M.login(jsonData) --解析json中的键和数据值 @@ -28,7 +16,7 @@ function _M.login(jsonData) local captcha = jsonData["captcha"] local checkKey = jsonData["checkKey"] --验证用户名是否为空 - local code, res = authenticate(name, passwd) + local code, res = userDao:getUserByUsername(name) --authenticate(name, passwd) if code ~= 0 then return 0x000001,res end @@ -36,12 +24,17 @@ function _M.login(jsonData) if res ~= nil then num = table.getn(res) end - --用户存在时返回用户已经存在 + --用户不存在时返回 if num <= 0 then return 0x01000C,nil end + --进行密码验证 获取数据库的密码 + local pwdMd5 = ngx.md5(res[1].password) + if pwdMd5 ~= passwd then + return 0x000001,res --密码错误 + end local userid = res[1].id - --获取用户id查询角色信息 + --通过用户id获取应用程序id和应用名称 local err, rest = userDao:userRole(userid) if rest == nil then return 0x01000C,nil @@ -67,4 +60,8 @@ function _M.getUser(userid) return userDao:getSystemUser(userid) end +function _M.getApplicationBy(client_id, redirect_uri) + return applicationDao.getApplicationByClientId(client_id, redirect_uri) +end + return _M \ No newline at end of file diff --git a/src/dao/system/application.lua b/src/dao/system/application.lua index ca66b5b..994d20e 100644 --- a/src/dao/system/application.lua +++ b/src/dao/system/application.lua @@ -104,4 +104,9 @@ function _M.updateApplication(id, jsonData) return applicationModel:where('id', '=', id):update(jsonData) end +--根据客户端id和重定向地址获取应用程序 +function _M.getApplicationByClientId(client_id, redirect_uri) + return applicationModel:where('app_id', '=', client_id):where('redirect_uris', '=', redirect_uri):get() +end + return _M \ No newline at end of file diff --git a/src/service/oauth/oauth.lua b/src/service/oauth/oauth.lua index 0126a47..068bdeb 100644 --- a/src/service/oauth/oauth.lua +++ b/src/service/oauth/oauth.lua @@ -16,17 +16,81 @@ local _M = {} function _M:authorize() --读取请求体的数据 ngx.req.read_body() + local args = ngx.req.get_uri_args() --获取请求数据 local body_data = ngx.req.get_body_data() - -- 验证数据是否符合json - local ok = validator.validatorAuthorize(body_data) + -- 验证json数据是否正确 + local ok, data = pcall(cjson.decode, body_data) + if not ok then + return ngx.exit(ngx.HTTP_BAD_REQUEST) + end + -- 校验客户端请求参数 + ok = validator.validatorAuthorize(data) --验证失败则返回 if not ok then - local result = resp:json(0x000001) - resp:send(result) + return ngx.exit(ngx.HTTP_BAD_REQUEST) + end + -- 校验 response_type 必须为 "code"(授权码模式) + if args.response_type ~= "code" then + return ngx.exit(ngx.HTTP_BAD_REQUEST) + end + -- 校验客户端id和redirect_uri是否存在数据库 + local code, res = authDao.getApplicationBy(args.client_id, args.redirect_uri) + if code ~= 0 or not res then + return ngx.exit(ngx.HTTP_UNAUTHORIZED) + end + -- 验证范围 + if args.scope then + local requested_scopes = {} + for scope in string.gmatch(args.scope, "%S+") do + table.insert(requested_scopes, scope) + end + -- 验证范围是否允许 todo + end + -- 判断用户登录检查 用户已登录,直接展示授权确认页;未登录则重定向到登录页 + local user_logged_in = false + if not user_logged_in then + -- 重定向到登录页,携带当前授权请求参数(登录后跳转回来) + local login_url = "/login?redirect=" .. ngx.escape_uri(ngx_var.request_uri) + ngx.redirect(login_url) return end - -- + + -- 4. 处理用户授权确认(用户点击"同意"后提交 POST 请求) + if ngx_req.get_method() == "POST" then + local post_args = ngx_req.get_post_args() + if post_args.action == "allow" then + -- 5. 生成授权码(随机字符串,确保唯一性) + 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 --根据授权码获取Access-Token @@ -173,29 +237,49 @@ 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 = validator.validatorLogin(body_data) - --验证失败则返回 + --验证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) + --验证失败则返回 + if not valid then + local result = resp:json(0x000001) + resp:send(result) + return + end + local code, ret = loginDao.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 = "11" + -- 用户验证成功后,返回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 = user_id.."-code" + -- 将code放入到共享内存中 + local key = code -- 作为键值 local shared_dict = ngx.shared.codeDict - shared_dict:set(key, code) - shared_dict:expire(key, 10) + shared_dict:set(key, user_id) + -- 数据时效性先设置5分钟 + shared_dict:expire(key, 5 * 60) --发送code到前端请求 local result = resp:json(0, code) resp:send(result)