将token时间增加到配置文件,增加回收token接口,修改刷新接口,并完善部分业务逻辑,修改refresh接口的验证
This commit is contained in:
parent
30fdac7cf8
commit
68337dc605
|
|
@ -75,9 +75,11 @@ http {
|
|||
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"}
|
||||
grant_types_supported = [ "authorization_code", "token", "refresh_token" ], -- 新增支持 refresh_token
|
||||
response_types_supported = { "code" },
|
||||
subject_types_supported = { "public" },
|
||||
id_token_signing_alg_values_supported = { "HS256" },
|
||||
refresh_token_issuance_supported = true -- 声明支持颁发 refresh_token
|
||||
}
|
||||
ngx.header["Content-Type"] = "application/json"
|
||||
ngx.say(cjson.encode(config))
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ local routes = {
|
|||
methods = { "POST" },
|
||||
handler = oauthService.userinfo,
|
||||
},
|
||||
--回收token
|
||||
{
|
||||
paths = { "/yum/v1/oauth/v2/logout" },
|
||||
methods = { "POST" },
|
||||
handler = oauthService.logout,
|
||||
},
|
||||
}
|
||||
|
||||
-- 初始化路由
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ local _M = {
|
|||
|
||||
secret_key = "!@#$5412$#@!", -- 确保这个密钥足够安全并保密
|
||||
|
||||
access_token_ttl = 10 * 60, --十分钟
|
||||
refresh_token_ttl = 7 * 24 * 3600, --7天
|
||||
id_token_ttl = 60 * 60, --1小时
|
||||
|
||||
REDIS_PREFIX = 'Auth:',
|
||||
-- 配置redis数据库连接
|
||||
REDIS = {
|
||||
|
|
|
|||
|
|
@ -169,6 +169,8 @@ local function authorizateCode(args)
|
|||
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
|
||||
client.createRefreshToken(user_id, client_id)
|
||||
-- 生存id_token
|
||||
local new_id_token = token.generate_id_token(priv_key, user_id, client_id, scope)
|
||||
--ngx.say("Generated JWT: ", jwt_obj)
|
||||
|
|
@ -193,13 +195,38 @@ end
|
|||
-- 刷新令牌
|
||||
local function authorizateRefresh(args)
|
||||
-- 1.校验必填参数验证数据是否符合json
|
||||
local ok = validator.validateRefresh(args)
|
||||
if not ok then
|
||||
local res = validator.validateRefresh(args)
|
||||
if not res then
|
||||
local result = resp:json(0x000001)
|
||||
resp:send(result)
|
||||
return
|
||||
end
|
||||
-- 2.
|
||||
-- 2.验证并消费 refresh_token(滚动刷新:生成新的 rt)
|
||||
local rt_data, err = client.consumeRefreshToken(res.refresh_token, res.client_id, true)
|
||||
if not rt_data then
|
||||
ngx.log(ngx.ERR, "refresh_token 验证失败: ", err)
|
||||
ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||
end
|
||||
|
||||
-- 生成新的 access_token 和 id_token
|
||||
local userinfo = {
|
||||
sub = rt_data.sub,
|
||||
name = "Test User",
|
||||
email = "test@example.com"
|
||||
}
|
||||
-- 3.生成新 Access Token
|
||||
local new_access_token = token.generate_access_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)
|
||||
local ret = {}
|
||||
ret.access_token = new_access_token
|
||||
ret.token_type = "Bearer"
|
||||
ret.expires_in = 10 * 60
|
||||
ret.id_token = new_id_token
|
||||
-- 4.返回结果
|
||||
local result = resp:json(ngx.HTTP_OK, ret)
|
||||
resp:send(result)
|
||||
end
|
||||
|
||||
-- 根据授权码获取Access-Token
|
||||
|
|
@ -238,6 +265,8 @@ function _M:token()
|
|||
authorizateCode(args)
|
||||
elseif grant_type == "refresh_token" then
|
||||
authorizateRefresh(args)
|
||||
else
|
||||
ngx.exit(ngx.HTTP_BAD_REQUEST)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -289,10 +318,10 @@ function _M:userinfo()
|
|||
ngx.exit(ngx.HTTP_UNAUTHORIZED)
|
||||
end
|
||||
--通过用户id获取用户信息
|
||||
print("-- get jwt_obj.payload value --")
|
||||
for key, value in pairs(jwt_obj.payload) do
|
||||
print("jwt_obj.payload: ", key, " ", value)
|
||||
end
|
||||
--print("-- get jwt_obj.payload value --")
|
||||
--for key, value in pairs(jwt_obj.payload) do
|
||||
-- print("jwt_obj.payload: ", key, " ", value)
|
||||
--end
|
||||
local user_id = jwt_obj.payload.sub
|
||||
local code, rest = oauthDao.getUser(user_id)
|
||||
--读取数据错误
|
||||
|
|
@ -313,4 +342,24 @@ function _M:userinfo()
|
|||
resp:send(result)
|
||||
end
|
||||
|
||||
--回收token
|
||||
function _M:logout()
|
||||
-- 假设从会话中获取用户 ID(sub)
|
||||
local sub = "user123" -- 实际应从登录会话中获取
|
||||
|
||||
-- 遍历所有 refresh_token(生产环境建议用 Redis 哈希存储用户与 rt 的映射)
|
||||
local keys = red:keys("oidc:refresh_token:*")
|
||||
for _, key in ipairs(keys) do
|
||||
local rt_data_str = red:get(key)
|
||||
if rt_data_str and rt_data_str ~= ngx.null then
|
||||
local rt_data = cjson.decode(rt_data_str)
|
||||
if rt_data.sub == sub then
|
||||
rt_data.revoked = true
|
||||
red:set(key, cjson.encode(rt_data))
|
||||
red:expire(key, 0) -- 立即过期
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return _M
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
local red = require("share.redis")
|
||||
local cjson = require("cjson.safe")
|
||||
local conf = require("config")
|
||||
|
||||
local _M = {}
|
||||
|
||||
|
|
@ -41,4 +42,77 @@ function _M.validate(client_id, redirect_uri)
|
|||
return client
|
||||
end
|
||||
|
||||
-- 生成随机 refresh_token(64字节,更长更安全)
|
||||
local function generate_refresh_token()
|
||||
local random_bytes = random.bytes(64, true) -- 强随机数
|
||||
return str.to_hex(random_bytes)
|
||||
end
|
||||
|
||||
local str = require "resty.string"
|
||||
local random = require "resty.random"
|
||||
|
||||
-- 存储 refresh_token(关联用户、客户端、过期时间)
|
||||
function _M.createRefreshToken(sub, client_id)
|
||||
local rt = generate_refresh_token()
|
||||
local rt_data = {
|
||||
sub = sub, -- 用户唯一标识
|
||||
client_id = client_id, -- 客户端ID
|
||||
expires_at = ngx.time() + conf.refresh_token_ttl,
|
||||
revoked = false -- 是否吊销
|
||||
}
|
||||
|
||||
-- 存储在 Redis:key=oidc:refresh_token:{rt}
|
||||
local ok, err = red:set("oidc:refresh_token:" .. rt, cjson.encode(rt_data))
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
red:expire("oidc:refresh_token:"..rt, conf.refresh_token_ttl) -- 设置过期时间
|
||||
return rt
|
||||
end
|
||||
|
||||
-- 验证并消费 refresh_token(支持一次性或滚动刷新)
|
||||
-- 若 rolling=true,则生成新的 refresh_token 并吊销旧的
|
||||
function _M.consumeRefreshToken(rt, client_id, rolling)
|
||||
-- 获取存储的 refresh_token 数据
|
||||
local rt_data_str, err = red:get("oidc:refresh_token:" .. rt)
|
||||
if not rt_data_str or rt_data_str == ngx.null then
|
||||
return nil, "无效的 refresh_token"
|
||||
end
|
||||
|
||||
local rt_data = cjson.decode(rt_data_str)
|
||||
|
||||
-- 验证客户端匹配
|
||||
if rt_data.client_id ~= client_id then
|
||||
return nil, "客户端不匹配"
|
||||
end
|
||||
|
||||
-- 验证未过期
|
||||
if rt_data.expires_at < ngx.time() then
|
||||
return nil, "refresh_token 已过期"
|
||||
end
|
||||
|
||||
-- 验证未被吊销
|
||||
if rt_data.revoked then
|
||||
return nil, "refresh_token 已吊销"
|
||||
end
|
||||
|
||||
-- 滚动刷新:生成新的 refresh_token,吊销旧的
|
||||
local new_rt = nil
|
||||
if rolling then
|
||||
new_rt, err = _M.create(rt_data.sub, client_id)
|
||||
if not new_rt then
|
||||
return nil, "生成新 refresh_token 失败: " .. (err or "")
|
||||
end
|
||||
-- 吊销旧的 refresh_token
|
||||
rt_data.revoked = true
|
||||
red:set("oidc:refresh_token:" .. rt, cjson.encode(rt_data))
|
||||
red:expire("oidc:refresh_token:" .. rt, 0) -- 立即过期
|
||||
end
|
||||
|
||||
return {
|
||||
sub = rt_data.sub,
|
||||
new_refresh_token = new_rt -- 新的 refresh_token(若滚动刷新)
|
||||
}
|
||||
end
|
||||
|
||||
return _M
|
||||
|
|
@ -21,13 +21,13 @@ local schema = {
|
|||
local obj = {
|
||||
header = { typ = "JWT", alg = "HS256" },
|
||||
payload = { -- 自定义数据
|
||||
userid = "", -- 用户id
|
||||
sub = "", -- 用户id
|
||||
username = "", -- 用户名
|
||||
role_id = "", -- 角色id
|
||||
role_name = "", -- 角色名称
|
||||
--iss = "your_issuer", -- 签发者
|
||||
--sub = "1234567890", -- 主题
|
||||
exp = ngx.time() + 3600, -- 过期时间(例如:当前时间+1小时)
|
||||
exp = ngx.time() + conf.access_token_ttl, -- 过期时间(例如:当前时间+1小时)
|
||||
iat = ngx.time() -- 签发时间
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ function _M.generateToken(userid, username, role_id, role_name)
|
|||
return ""
|
||||
end
|
||||
|
||||
obj.payload.userid = userid
|
||||
obj.payload.sub = userid
|
||||
obj.payload.username = username
|
||||
obj.payload.role_id = role_id
|
||||
obj.payload.role_name = role_name
|
||||
|
|
@ -93,10 +93,6 @@ function _M.authorizationToken(auth_header)
|
|||
return response
|
||||
end
|
||||
|
||||
local access_token_ttl = 10 * 60 --十分钟
|
||||
local refresh_token_ttl = 7 * 24 * 3600 --7天
|
||||
local id_token_ttl = 60 * 60 --1小时
|
||||
|
||||
-- 生成 Access Token(简化为 JWT 格式)
|
||||
function _M.generate_access_token(priv_key, sub, client_id, scope)
|
||||
local now = ngx.time()
|
||||
|
|
@ -104,7 +100,7 @@ function _M.generate_access_token(priv_key, sub, client_id, scope)
|
|||
--iss = OP_DOMAIN,
|
||||
sub = sub,
|
||||
aud = client_id,
|
||||
exp = now + access_token_ttl,
|
||||
exp = now + conf.access_token_ttl,
|
||||
iat = now,
|
||||
scope = scope, --"openid profile email"
|
||||
jti = ngx.md5(now .. math.random() .. client_id) -- 唯一标识
|
||||
|
|
@ -123,7 +119,7 @@ function _M.generate_refresh_token(priv_key, sub, client_id, scope)
|
|||
--iss = OP_DOMAIN,
|
||||
sub = sub,
|
||||
aud = client_id,
|
||||
exp = now + refresh_token_ttl,
|
||||
exp = now + conf.refresh_token_ttl,
|
||||
iat = now,
|
||||
scope = scope, --"openid profile email"
|
||||
jti = ngx.md5(now .. math.random() * 1000 .. client_id)
|
||||
|
|
@ -142,7 +138,7 @@ function _M.generate_id_token(priv_key, sub, client_id, userinfo, scope)
|
|||
--iss = OP_DOMAIN, -- issuer:OP 域名
|
||||
sub = sub, -- subject:用户唯一标识
|
||||
aud = client_id, -- audience:客户端 ID
|
||||
exp = now + id_token_ttl, -- 过期时间(1小时)
|
||||
exp = now + conf.id_token_ttl, -- 过期时间(1小时)
|
||||
iat = now, -- 签发时间
|
||||
nonce = ngx.var.nonce, -- 可选:防重放攻击
|
||||
--name = userinfo.name,
|
||||
|
|
|
|||
|
|
@ -87,10 +87,11 @@ local schemaRefresh = {
|
|||
type = "object",
|
||||
properties = {
|
||||
grant_type = { type = "string" },
|
||||
refresh_token = { type = "string" },
|
||||
client_id = { type = "string" },
|
||||
client_secret = { type = "string" },
|
||||
},
|
||||
required = { "grant_type", "client_id", "client_secret" }
|
||||
required = { "grant_type", "refresh_token", "client_id", "client_secret" }
|
||||
}
|
||||
|
||||
--根据Refresh-Token刷新Access-Token
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user