Compare commits
2 Commits
54cc8fbfbf
...
de9034fbc6
| Author | SHA1 | Date | |
|---|---|---|---|
| de9034fbc6 | |||
|
|
b286ecf891 |
|
|
@ -8,6 +8,29 @@ local applicationDao = require("dao.system.application")
|
|||
|
||||
local _M = {}
|
||||
|
||||
--通过用户名和密码进行认证
|
||||
function _M.authenticateUserPasswd(username, passwd)
|
||||
--验证用户名是否为空
|
||||
local code, res = userDao:getUserByUsername(username) --authenticate(name, passwd)
|
||||
if code ~= 0 then
|
||||
return 0x000001, res
|
||||
end
|
||||
local num = 0
|
||||
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
|
||||
return 0, res
|
||||
end
|
||||
|
||||
--用户登录业务逻辑处理
|
||||
function _M.login(jsonData)
|
||||
--解析json中的键和数据值
|
||||
|
|
@ -65,4 +88,9 @@ function _M.getApplicationBy(client_id, redirect_uri)
|
|||
return applicationDao.getApplicationByClientId(client_id, redirect_uri)
|
||||
end
|
||||
|
||||
function _M.getApplicationByUserid(user_id, client_id, client_secret)
|
||||
--print("getApplicationBy user_id:", user_id, " client_id:", client_id, "client_secret", client_secret)
|
||||
return applicationDao.getApplicationByUserid(user_id, client_id, client_secret)
|
||||
end
|
||||
|
||||
return _M
|
||||
|
|
@ -110,4 +110,10 @@ function _M.getApplicationByClientId(client_id, redirect_uri)
|
|||
return applicationModel:where('app_id', '=', client_id):where('redirect_uris', '=', redirect_uri):get()
|
||||
end
|
||||
|
||||
function _M.getApplicationByUserid(user_id, client_id, client_secret)
|
||||
local sql = [[SELECT "A".user_id,"B".* FROM sys_user_application AS "A" INNER JOIN sys_application AS "B" ON "A".application_id = "B"."id"
|
||||
WHERE "A".user_id =']]..user_id..[[' AND "B".client_id=']]..client_id..[[' AND "B".client_secret=']]..client_secret.."'"
|
||||
return applicationModel:exec(sql)
|
||||
end
|
||||
|
||||
return _M
|
||||
|
|
@ -11,6 +11,7 @@ local jwt = require "resty.jwt"
|
|||
local rsa = require("util.rsa")
|
||||
local authcode = require("util.authcode")
|
||||
local token = require("util.token")
|
||||
local client = require("util.client")
|
||||
|
||||
local _M = {}
|
||||
|
||||
|
|
@ -39,7 +40,9 @@ function _M:authorize()
|
|||
return ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||
end
|
||||
-- 1、校验客户端id和redirect_uri是否存在数据库
|
||||
local code, res = oauthDao.getApplicationBy(args.client_id, args.redirect_uri)
|
||||
local client_id = args.client_id
|
||||
local redirect_uri = args.redirect_uri
|
||||
local code, res = oauthDao.getApplicationBy(client_id, redirect_uri)
|
||||
if code ~= 0 or not res then
|
||||
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||
end
|
||||
|
|
@ -52,8 +55,8 @@ function _M:authorize()
|
|||
-- 验证范围是否允许 todo
|
||||
end
|
||||
-- 3、判断用户登录检查 用户已登录,直接展示授权确认页;未登录则重定向到登录页
|
||||
local user_logged_in = true
|
||||
if not user_logged_in then
|
||||
local user, err = client.validate(client_id, redirect_uri)
|
||||
if user == nil then
|
||||
-- 重定向到登录页,携带当前授权请求参数(登录后跳转回来)
|
||||
local login_url = "/login?redirect=" .. ngx.escape_uri(ngx.var.request_uri)
|
||||
--print("authorize login_url:", login_url)
|
||||
|
|
@ -63,7 +66,7 @@ function _M:authorize()
|
|||
return
|
||||
end
|
||||
-- 4. 生成授权码(随机字符串,确保唯一性)(用户ID、客户端ID、scope、生成时间)
|
||||
local auth_code, err = authcode.create("123456", args.client_id, args.redirect_uri, args.scope)
|
||||
local auth_code, err = authcode.create(user.userid, client_id, redirect_uri, args.scope)
|
||||
if not auth_code then
|
||||
ngx.log(ngx.ERR, "生成授权码失败: ", err)
|
||||
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
|
|
@ -72,15 +75,112 @@ function _M:authorize()
|
|||
-- 5. 重定向到客户端回调地址,携带授权码和原始 state(防 CSRF)
|
||||
local redirect_url = args.redirect_uri .. "?code=" .. code .. "&state=" .. args.state
|
||||
local rest = {}
|
||||
rest.redirect_uri = args.redirect_uri
|
||||
rest.redirect_uri = redirect_uri
|
||||
rest.code = auth_code
|
||||
rest.state = args.state
|
||||
local result = resp:json(ngx.HTTP_OK, rest)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
|
||||
--根据授权码获取Access-Token
|
||||
-- 通过用户名认证用户和应用是否存在状态
|
||||
local function authorizatePassword(args)
|
||||
-- 1.校验必填参数验证数据是否符合json
|
||||
local ok = validator.validateLogin(args)
|
||||
if not ok then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
-- 2.验证用户名和密码,应用程序id和应用程序密钥
|
||||
local code, res = oauthDao.authenticateUserPasswd(args.username, args.password)
|
||||
if code ~= 0 or res == nil then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
-- 3.对当前用户的持有的应用程序进行验证
|
||||
local userid = res[1].id
|
||||
local client_id = args.client_id
|
||||
local client_secret = args.client_secret
|
||||
code, res = oauthDao.getApplicationByUserid(userid, client_id, client_secret)
|
||||
if code ~= 0 or res == nil then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
local redirect_uri = res[1].redirect_uris
|
||||
-- 4.生成授权码(随机字符串,确保唯一性)(用户ID、客户端ID、scope、生成时间)
|
||||
local auth_code, err = authcode.create(userid, client_id, redirect_uri)
|
||||
if not auth_code then
|
||||
ngx.log(ngx.ERR, "生成授权码失败: ", err)
|
||||
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
-- 5.存储用户信息,证明用户已经登陆
|
||||
client.create(userid, client_id, redirect_uri)
|
||||
-- 6.返回结果
|
||||
local rest = {}
|
||||
rest.redirect_uri = redirect_uri
|
||||
rest.code = auth_code
|
||||
local result = resp:json(ngx.HTTP_OK, rest)
|
||||
resp:send(result)
|
||||
end
|
||||
|
||||
-- 通过code形式进行认证
|
||||
local function authorizateCode(args)
|
||||
-- 1.校验必填参数验证数据是否符合json
|
||||
local ok = validator.validateToken(args)
|
||||
if not ok then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
-- 2.校验 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
|
||||
-- 3.验证redirect_url地址的正确性
|
||||
local request_uri = code_data.redirect_uri
|
||||
print("token request_uri:", request_uri)
|
||||
if request_uri ~= args.redirect_uri then
|
||||
print("token redirect_url:", request_uri, args.redirect_uri)
|
||||
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
|
||||
-- 4.生成密钥对
|
||||
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("token pubkey:", pub_key)
|
||||
local user_id = code_data.user_id
|
||||
local client_id = code_data.client_id
|
||||
local scope = code_data.scope
|
||||
-- 5.生成新 Access Token
|
||||
local new_access_token = token.generate_access_token(priv_key, user_id, client_id, scope)
|
||||
-- 生成新 Refresh Token(滚动刷新)
|
||||
local new_refresh_token = token.generate_refresh_token(priv_key, user_id, client_id, scope)
|
||||
-- 生存id_token
|
||||
local new_id_token = token.generate_id_token(priv_key, user_id, client_id, scope)
|
||||
--ngx.say("Generated JWT: ", jwt_obj)
|
||||
-- 6.返回结果
|
||||
local ret = {}
|
||||
ret.access_token = new_access_token
|
||||
ret.token_type = "Bearer"
|
||||
ret.expires_in = 10 * 60
|
||||
ret.refresh_token = new_refresh_token
|
||||
ret.id_token = new_id_token
|
||||
local result = resp:json(ngx.HTTP_OK, ret)
|
||||
resp:send(result)
|
||||
end
|
||||
|
||||
-- 根据授权码获取Access-Token
|
||||
function _M:token()
|
||||
-- 1. 解析请求参数(支持 form-data 和 json)
|
||||
local content_type = ngx.req.get_headers()["Content-Type"] or ""
|
||||
|
|
@ -101,61 +201,20 @@ function _M:token()
|
|||
end
|
||||
args = data
|
||||
else
|
||||
-- 默认解析 form-urlencoded
|
||||
args = ngx.req.get_post_args()
|
||||
if ngx.req.get_method() == "POST" then
|
||||
-- 默认解析 form-urlencoded
|
||||
args = ngx.req.get_post_args()
|
||||
elseif ngx.req.get_method() == "GET" then
|
||||
args = ngx.req.get_uri_args()
|
||||
end
|
||||
end
|
||||
|
||||
-- 2. 校验必填参数验证数据是否符合json
|
||||
local ok = validator.validateToken(args)
|
||||
if not ok then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
local grant_type = args.grant_type
|
||||
if grant_type == "password" then
|
||||
authorizatePassword(args)
|
||||
elseif grant_type == "authorization_code" then
|
||||
authorizateCode(args)
|
||||
end
|
||||
-- 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
|
||||
-- 4、验证redirect_url地址的正确性
|
||||
local request_uri = code_data.redirect_uri
|
||||
print("token request_uri:", request_uri)
|
||||
if request_uri ~= args.redirect_uri then
|
||||
print("token redirect_url:", request_uri, args.redirect_uri)
|
||||
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
|
||||
-- 5. 生成密钥对
|
||||
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("token pubkey:", pub_key)
|
||||
local user_id = code_data.user_id
|
||||
local client_id = code_data.client_id
|
||||
local scope = code_data.scope
|
||||
-- 6 生成新 Access Token
|
||||
local new_access_token = token.generate_access_token(priv_key, user_id, client_id, scope)
|
||||
-- 7 生成新 Refresh Token(滚动刷新)
|
||||
local new_refresh_token = token.generate_refresh_token(priv_key, user_id, client_id, scope)
|
||||
-- 8、生存id_token
|
||||
local new_id_token = token.generate_id_token(priv_key, user_id, client_id, scope)
|
||||
--ngx.say("Generated JWT: ", jwt_obj)
|
||||
-- 9. 返回结果
|
||||
local ret = {}
|
||||
ret.access_token = new_access_token
|
||||
ret.token_type = "Bearer"
|
||||
ret.expires_in = 10 * 60
|
||||
ret.refresh_token = new_refresh_token
|
||||
ret.id_token = new_id_token
|
||||
local result = resp:json(ngx.HTTP_OK, ret)
|
||||
resp:send(result)
|
||||
end
|
||||
|
||||
--根据Access-Token获取相应用户的账户信息
|
||||
|
|
@ -181,15 +240,15 @@ function _M:userinfo()
|
|||
-- 3.获取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
|
||||
-- 5.对token进行验证
|
||||
print("userinfo pubkey:", pub_key)
|
||||
--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
|
||||
-- 4.对token进行验证
|
||||
--print("userinfo pubkey:", pub_key)
|
||||
local jwt_obj = jwt:verify(pub_key, token)
|
||||
--如果校验结果中的verified==false,则表示令牌无效
|
||||
if jwt_obj.verified == false then
|
||||
|
|
@ -204,14 +263,23 @@ function _M:userinfo()
|
|||
ngx.status = ngx.HTTP_UNAUTHORIZED
|
||||
ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||
end
|
||||
-- 6.获取token中的信息进行所需用户的信息返回
|
||||
--通过用户id获取用户信息
|
||||
local user_id = jwt_obj.payload.sub
|
||||
local code, rest = oauthDao.getUser(user_id)
|
||||
--读取数据错误
|
||||
if code ~= 0 or table.getn(ret) < 0 then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
-- 5.获取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"
|
||||
ret.sub = user_id
|
||||
ret.name = rest[1].username
|
||||
ret.phone = rest[1].phone
|
||||
ret.real_name = rest[1].realname
|
||||
ret.office_phone = rest[1].office_phone
|
||||
ret.email = rest[1].email
|
||||
local result = resp:json(ngx.HTTP_OK, ret)
|
||||
resp:send(result)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ end
|
|||
|
||||
-- 验证并消费授权码(一次性有效)
|
||||
function _M.consume(code, client_id)
|
||||
if code == nil then
|
||||
return nil, "无效的授权码"
|
||||
end
|
||||
local code_key = "auth_code-"..code
|
||||
local shared_dict = ngx.shared.codeDict
|
||||
local data = shared_dict:get(code_key)
|
||||
|
|
|
|||
44
src/util/client.lua
Normal file
44
src/util/client.lua
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
--- Generated by EmmyLua(https://github.com/EmmyLua)
|
||||
--- Created by admin.
|
||||
--- DateTime: 2025/11/14 13:46
|
||||
--- OpenIDC协议中OAutho2.0判断第三方用户是否登录系统
|
||||
|
||||
local red = require("share.redis")
|
||||
local cjson = require("cjson.safe")
|
||||
|
||||
local _M = {}
|
||||
|
||||
-- 客户端登录进行存储
|
||||
function _M:create(userid, client_id, redirect_uris)
|
||||
local client_str = {}
|
||||
client_str.userid = userid
|
||||
client_str.client_id = client_id
|
||||
client_str.redirect_uris = redirect_uris
|
||||
red:set("oidc:client:"..client_id, client_str)
|
||||
end
|
||||
|
||||
-- 验证客户端是否存在
|
||||
function _M.validate(client_id, redirect_uri)
|
||||
-- 从 Redis 获取客户端信息(假设存储在 key: oidc:client:{client_id})
|
||||
local client_str, err = red:get("oidc:client:"..client_id)
|
||||
if not client_str or client_str == ngx.nul6l then
|
||||
return nil, "客户端不存在"
|
||||
end
|
||||
|
||||
local client = cjson.decode(client_str)
|
||||
-- 验证 redirect_uri 是否在客户端注册的范围内
|
||||
local valid_redirect = false
|
||||
for _, uri in ipairs(client.redirect_uris) do
|
||||
if uri == redirect_uri then
|
||||
valid_redirect = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not valid_redirect then
|
||||
return nil, "无效的 redirect_uri"
|
||||
end
|
||||
return client
|
||||
end
|
||||
|
||||
return _M
|
||||
|
|
@ -28,6 +28,27 @@ function _M.validateAuthorize(jsonData)
|
|||
return result
|
||||
end
|
||||
|
||||
-- 定义一个JSON Schema
|
||||
local schemaLogin = {
|
||||
type = "object",
|
||||
properties = {
|
||||
grant_type = { type = "string" },
|
||||
client_id = { type = "string" },
|
||||
client_secret = { type = "string" },
|
||||
username = { type = "string" },
|
||||
password = { type = "string" },
|
||||
},
|
||||
required = { "grant_type", "client_id", "client_secret", "username", "password" }
|
||||
}
|
||||
|
||||
--通过用户名和密码进行认证
|
||||
function _M.validateLogin(jsonData)
|
||||
-- 验证数据是否符合schema
|
||||
local validator = jsonschema.generate_validator(schemaLogin)
|
||||
local result = validator(jsonData)
|
||||
return result
|
||||
end
|
||||
|
||||
local schemaToken = {
|
||||
type = "object",
|
||||
properties = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user