jwt-blacklist
扩展官方jwt插件,保留token过期及签名验证, 增加根据jti查询redis黑名单数据库,实现单一登录和强制下线。
扩展功能
- 增加可配置在header中添加userId
- 增加可配置的在转发到上游服务前,删除header中的jwt
- 优化配置参数,删除从cookie获取token,支持从url和header获取token,增加redis配置,增加默认值。
- 验证payload是否含有jti,没有返回401。
- 验证jti是否在黑名单,若在返回401和对应的value。
注意
- 此插件依赖官方jwt插件读写数据库,不能单独使用
- 插件支持解析如下jwt格式
{
"sub": "k4usr2v6an",
"user_name": "球场",
"origin": {
"status": "AccountVerification",
"msg": "账号已验证",
"users": [
{
"userId": "k4usr2v6an",
"type": "MOBILE",
"account": "130******71",
"mobile": "130******71",
"loginName": "球场",
"nickName": "球场"
}
]
},
"scope": [
"read",
"write"
],
"exp": 1590635506,
"authorities": [
"USER"
],
"jti": "20220bd3-a0c9-4318-bb35-c07e9611668c",
"client_id": "pc"
}
{
"exp": 1590603064,
"user_name": "k1jdxqwrda",
"authorities": [
"ROLE_DEVELOPER",
"ROLE_STUDENT"
],
"jti": "c8365700-0791-45d6-9e4d-ad1a669815bf",
"client_id": "dev-client",
"scope": [
"read",
"write"
]
}
使用
1. 增加一个service,url为http://mockbin.org/request

2. 增加一个router,path为/mock

3. 增加jwt插件,配置如下


4. 调用我们的授权服务获取一个access_token

5. 添加一个CONSUMER,配置jwt key和公钥

6. 在redis数据库中增加key为prefix+jti

7. 携带token访问service,验证结果

核心代码
{ redis_host = {
type = "string",
required = true,
default = "127.0.0.1"
} },
{ redis_port = {
type = "number",
required = true,
default = 6379
} },
{ redis_db = {
type = "number",
required = true,
default = 0
} },
{ redis_password = {
type = "string",
} },
{ string_key_prefix = {
type = "string",
required = true,
default = "userOffline:"
} },
function checkJti(conf, jti)
if not jti then
return false, { status = 401, message = "Invalid jti in claims" }
end
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect(conf.redis_host, conf.redis_port)
if not ok then
kong.log.err("failed to connect : ", err)
return
end
if conf.redis_password and conf.redis_password ~= "" then
local count
count, err = red:get_reused_times()
if 0 == count then
ok, err = red:auth(conf.redis_password)
if not ok then
kong.log.err("failed to auth: ", err)
return
end
elseif err then
kong.log.err("failed to get reused times: ", err)
return
end
end
ok, err = red:select(conf.redis_db)
if not ok then
kong.log.err("failed to select db: ", err)
return
end
-- get key
local res, err = red:get(conf.string_key_prefix .. jti)
kong.log.notice("red:get:", conf.string_key_prefix .. jti, ",value=", res)
if err then
kong.log.err("failed to get key", conf.string_key_prefix .. jti)
end
--连接池大小是100个,并且设置最大的空闲时间是 10 秒
ok, err = red:set_keepalive(10000, 100)
if not ok then
kong.log.err("failed to set keepalive: ", err)
return
end
if res ~= ngx.null then
return false, { status = 401, message = res }
end
return true
end
来源:oschina
链接:https://my.oschina.net/wecanweup/blog/4279853