飛書 + Lua 實現企業級組織架構登入認證
飛書是位元組跳動旗下一款企業級協同辦公軟體,本文將介紹如何基於飛書開放平臺的身份驗證能力,使用 Lua 實現企業級組織架構的登入認證閘道器。
登入流程
讓我們首先看一下飛書第三方網站免登的整體流程:
第一步: 網頁後端發現使用者未登入,請求身份驗證; 第二步: 使用者登入後,開放平臺生成登入預授權碼,302跳轉至重定向地址; 第三步: 網頁後端呼叫獲取登入使用者身份校驗登入預授權碼合法性,獲取到使用者身份; 第四步: 如需其他使用者資訊,網頁後端可呼叫獲取使用者資訊(身份驗證)。
瀏覽器內網頁登入
Lua 實現
飛書介面部分實現
獲取應用的 access_token
function _M:get_app_access_token()
local url = ""
local body = {
app_id = self.app_id,
app_secret = self.app_secret
}
local res, err = http_post(url, body, nil)
if not res then
return nil, err
end
if res.status ~= 200 then
return nil, res.body
end
local data = json.decode(res.body)
if data["code"] ~= 0 then
return nil, res.body
end
return data["tenant_access_token"]
end
透過回撥 code 獲取登入使用者資訊
function _M:get_login_user(code)
local app_access_token, err = self:get_app_access_token()
if not app_access_token then
return nil, "get app_access_token failed: " .. err
end
local url = ""
local headers = {
Authorization = "Bearer " .. app_access_token
}
local body = {
grant_type = "authorization_code",
code = code
}
ngx.log(ngx.ERR, json.encode(body))
local res, err = http_post(url, body, headers)
if not res then
return nil, err
end
local data = json.decode(res.body)
if data["code"] ~= 0 then
return nil, res.body
end
return data["data"]
end
獲取使用者詳細資訊
獲取登入使用者資訊時無法獲取到使用者的部門資訊,故這裡需要使用登入使用者資訊中的 open_id
獲取使用者的詳細資訊,同時 user_access_token
也是來自於獲取到的登入使用者資訊。
function _M:get_user(user_access_token, open_id)
local url = "" .. open_id
local headers = {
Authorization = "Bearer " .. user_access_token
}
local res, err = http_get(url, nil, headers)
if not res then
return nil, err
end
local data = json.decode(res.body)
if data["code"] ~= 0 then
return nil, res.body
end
return data["data"]["user"], nil
end
登入資訊
JWT 登入憑證
我們使用 JWT 作為登入憑證,同時用於儲存使用者的 open_id
和 department_ids
。
-- 生成 token
function _M:sign_token(user)
local open_id = user["open_id"]
if not open_id or open_id == "" then
return nil, "invalid open_id"
end
local department_ids = user["department_ids"]
if not department_ids or type(department_ids) ~= "table" then
return nil, "invalid department_ids"
end
return jwt:sign(
self.jwt_secret,
{
header = {
typ = "JWT",
alg = jwt_header_alg,
exp = ngx.time() + self.jwt_expire
},
payload = {
open_id = open_id,
department_ids = json.encode(department_ids)
}
}
)
end
-- 驗證與解析 token
function _M:verify_token()
local token = ngx.var.cookie_feishu_auth_token
if not token then
return nil, "token not found"
end
local result = jwt:verify(self.jwt_secret, token)
ngx.log(ngx.ERR, "jwt_obj: ", json.encode(result))
if result["valid"] then
local payload = result["payload"]
if payload["department_ids"] and payload["open_id"] then
return payload
end
return nil, "invalid token: " .. json.encode(result)
end
return nil, "invalid token: " .. json.encode(result)
end
使用 Cookie 儲存登入憑證
ngx.header["Set-Cookie"] = self.cookie_key .. "=" .. token
組織架構白名單
我們在使用者登入時獲取使用者的部門資訊,或者在使用者後續訪問應用時解析登入憑證中的部門資訊,根據設定的部門白名單,判斷使用者是否擁有訪問應用的許可權。
-- 部門白名單配置
_M.department_whitelist = {}
function _M:check_user_access(user)
if type(self.department_whitelist) ~= "table" then
ngx.log(ngx.ERR, "department_whitelist is not a table")
return false
end
if #self.department_whitelist == 0 then
return true
end
local department_ids = user["department_ids"]
if not department_ids or department_ids == "" then
return false
end
if type(department_ids) ~= "table" then
department_ids = json.decode(department_ids)
end
for i=1, #department_ids do
if has_value(self.department_whitelist, department_ids[i]) then
return true
end
end
return false
end
更多閘道器配置
同時支援 IP 黑名單和路由白名單配置。
-- IP 黑名單配置
_M.ip_blacklist = {}
-- 路由白名單配置
_M.uri_whitelist = {}
function _M:auth()
local request_uri = ngx.var.uri
ngx.log(ngx.ERR, "request uri: ", request_uri)
if has_value(self.uri_whitelist, request_uri) then
ngx.log(ngx.ERR, "uri in whitelist: ", request_uri)
return
end
local request_ip = ngx.var.remote_addr
if has_value(self.ip_blacklist, request_ip) then
ngx.log(ngx.ERR, "forbided ip: ", request_ip)
return ngx.exit(ngx.HTTP_FORBIDDEN)
end
if request_uri == self.logout_uri then
return self:logout()
end
local payload, err = self:verify_token()
if payload then
if self:check_user_access(payload) then
return
end
ngx.log(ngx.ERR, "user access not permitted")
self:clear_token()
return self:sso()
end
ngx.log(ngx.ERR, "verify token failed: ", err)
if request_uri ~= self.callback_uri then
return self:sso()
end
return self:sso_callback()
end
使用
本文就不贅述 OpenResty 的安裝了,可以參考我的另一篇文章《在 Ubuntu 上使用原始碼安裝 OpenResty》。
下載
cd /path/to
git clone git@github.com:ledgetech/lua-resty-http.git
git clone git@github.com:SkyLothar/lua-resty-jwt.git
git clone git@github.com:k8scat/lua-resty-feishu-auth.git
配置
lua_package_path "/path/to/lua-resty-feishu-auth/lib/?.lua;/path/to/lua-resty-jwt/lib/?.lua;/path/to/lua-resty-http/lib/?.lua;/path/to/lua-resty-redis/lib/?.lua;/path/to/lua-resty-redis-lock/lib/?.lua;;"; server { access_by_lua_block { local feishu_auth = require "resty.feishu_auth" feishu_auth.app_id = "" feishu_auth.app_secret = "" feishu_auth.callback_uri = "/feishu_auth_callback" feishu_auth.logout_uri = "/feishu_auth_logout" feishu_auth.app_domain = "feishu-auth.example.com" feishu_auth.jwt_secret = "thisisjwtsecret" feishu_auth.ip_blacklist = {"47.1.2.3"} feishu_auth.uri_whitelist = {"/"} feishu_auth.department_whitelist = {"0"} feishu_auth:auth() } }
配置說明
app_id
用於設定飛書企業自建應用的App ID
app_secret
用於設定飛書企業自建應用的App Secret
callback_uri
用於設定飛書網頁登入後的回撥地址(需在飛書企業自建應用的安全設定中設定重定向 URL)logout_uri
用於設定登出地址app_domain
用於設定訪問域名(需和業務服務的訪問域名一致)jwt_secret
用於設定 JWT secretip_blacklist
用於設定 IP 黑名單uri_whitelist
用於設定地址白名單,例如首頁不需要登入認證department_whitelist
用於設定部門白名單(字串)
應用許可權說明
獲取部門基礎資訊
獲取部門組織架構資訊
以應用身份讀取通訊錄
獲取使用者組織架構資訊
獲取使用者基本資訊
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/810/viewspace-2807088/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Delphi LDAP物件管理(使用者登入認證、組、組織)LDA物件
- 企業網盤為企業搭建統一組織架構架構
- JWT實現登入認證例項JWT
- 企業微信針對百萬級組織架構的客戶端效能最佳化實踐架構客戶端
- LR.Java低程式碼自主搭建企業組織架構Java架構
- TUV萊茵為時代吉利頒發組織碳中和認證證書
- puppet組織架構架構
- 企業與組織首選的SSL證書型別是什麼型別
- PHP如何實現登入認證和鑑權PHP
- 微服務架構學習與思考(07):企業團隊組織架構如何變革?微服務架構
- 企業級證書和個人證書的區別
- 整合spring cloud雲架構 - SSO單點登入之OAuth2.0登入認證SpringCloud架構OAuth
- Spring Cloud雲架構 - SSO單點登入之OAuth2.0登入認證(1)SpringCloud架構OAuth
- vsftpd實現mysql-pam認證登入薦FTPMySql
- 實戰!Spring Boot Security+JWT前後端分離架構登入認證!Spring BootJWT後端架構
- 實戰!spring Boot security+JWT 前後端分離架構認證登入!Spring BootJWT後端架構
- 企業IT組織的治理
- 整合spring cloud雲架構 - SSO單點登入之OAuth2.0登入認證(1)SpringCloud架構OAuth
- 企業級大資料中臺架構實戰大資料架構
- Spring Boot 整合 Sa-Token 實現登入認證Spring Boot
- 實戰模擬│JWT 登入認證JWT
- SOA架構實踐首先從企業級IT架構設計著手架構
- LR低程式碼快速開發平臺 高效調整企業組織架構架構
- .NET應用架構設計—重新認識分層架構(現代企業級應用分層架構核心設計要素)應用架構
- 企業級大資料中臺架構實戰【1】大資料架構
- 企業級大資料中臺架構實戰【3】大資料架構
- 梳理公司的組織架構 — 組合模式架構模式
- 梳理公司的組織架構 --- 組合模式架構模式
- ASP.NET實現企業微信接入應用實現身份認證ASP.NET
- 【Serverless】Unity快速整合認證服務實現郵件登入ServerUnity
- SAP Organizational Structure Overview(組織架構)StructView架構
- AOP實現Android集中式登入架構Android架構
- LAMMP架構的企業級應用架構
- 組織架構新型資料結構思考架構資料結構
- 賬戶 登入 被組織管理 解除
- 企業WiFi認證,如何保證企業WiFi安全?WiFi
- Spring Security——基於表單登入認證原理及實現Spring
- ASP.NET Core Authentication系列(二)實現認證、登入和登出ASP.NET