一、为什么需要单点登录
想象一下,你每天上班要登录十几个系统,每个系统都要输入账号密码。光是记住这些密码就够头疼了,更别提频繁切换带来的效率问题。这就是单点登录(SSO)要解决的问题 - 一次登录,处处通行。
在企业环境中,通常会有多个内部系统:OA、CRM、ERP等等。如果每个系统都维护自己的账号体系,不仅管理麻烦,用户体验也很差。SSO就像一把万能钥匙,解决了这个痛点。
二、OpenResty是什么
OpenResty不是个新东西,它本质上是在Nginx基础上打了个"增强补丁"。通过内置LuaJIT引擎,让我们可以用Lua脚本扩展Nginx的功能。就像给普通汽车装上赛车引擎,外表还是那个Nginx,但性能和处理能力完全不一样了。
特别适合做SSO是因为:
- 高性能 - 轻松应对高并发认证请求
- 灵活性 - Lua脚本可以自定义各种认证逻辑
- 轻量级 - 相比传统Java方案更节省资源
三、SSO的核心实现方案
3.1 基于Cookie的方案
最直观的做法是利用浏览器Cookie。认证中心颁发令牌后,各个子系统通过校验这个令牌来判断用户身份。
-- OpenResty Lua示例:校验Cookie中的token
location /protected {
access_by_lua_block {
local token = ngx.var.cookie_AuthToken
if not token then
ngx.redirect("/sso/login?return="..ngx.var.request_uri)
end
-- 调用认证中心验证token有效性
local res = ngx.location.capture("/auth/verify?token="..token)
if res.status ~= 200 then
ngx.redirect("/sso/login")
end
-- 验证通过,设置用户信息到header
ngx.req.set_header("X-User-Id", res.body.user_id)
}
}
3.2 基于JWT的方案
JWT(JSON Web Token)是更现代的方案。令牌本身包含用户信息,避免了频繁查询用户数据库。
-- OpenResty Lua示例:JWT验证
local jwt = require "resty.jwt"
location /api {
access_by_lua_block {
local auth_header = ngx.var.http_Authorization
if not auth_header then
return ngx.exit(401)
end
-- 提取Bearer token
local _, _, token = string.find(auth_header, "Bearer%s+(.+)")
if not token then
return ngx.exit(401)
end
-- 验证JWT签名
local jwt_obj = jwt:verify("your-secret-key", token)
if not jwt_obj.verified then
return ngx.exit(403)
end
-- 检查过期时间
if jwt_obj.payload.exp < os.time() then
return ngx.exit(401)
end
}
}
四、完整实现示例
让我们看一个完整的SSO实现,包含认证中心和子系统集成。
4.1 认证中心实现
-- OpenResty Lua示例:认证中心核心逻辑
location /sso/login {
content_by_lua_block {
-- 检查是否已登录
local token = ngx.var.cookie_AuthToken
if token then
local res = ngx.location.capture("/auth/verify?token="..token)
if res.status == 200 then
local return_url = ngx.var.arg_return or "/"
return ngx.redirect(return_url)
end
end
-- 显示登录页
ngx.say([[
<form action="/sso/auth" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="hidden" name="return" value="]]..(ngx.var.arg_return or "")..[[">
<button type="submit">登录</button>
</form>
]])
}
}
location = /sso/auth {
content_by_lua_block {
-- 验证用户名密码
ngx.req.read_body()
local args = ngx.req.get_post_args()
-- 这里应该是数据库验证,简化为示例
if args.username ~= "admin" or args.password ~= "123456" then
return ngx.redirect("/sso/login?error=1")
end
-- 生成token
local token = ngx.md5(args.username .. os.time() .. math.random(1000))
-- 存储token到Redis
local redis = require "resty.redis"
local red = redis:new()
red:set("sso:token:"..token, args.username)
red:expire("sso:token:"..token, 3600) -- 1小时过期
-- 设置Cookie并跳转
ngx.header["Set-Cookie"] = "AuthToken="..token.."; Path=/; HttpOnly"
return ngx.redirect(args.return or "/")
}
}
4.2 子系统集成
子系统只需要在Nginx配置中添加前置验证:
location / {
access_by_lua_block {
local token = ngx.var.cookie_AuthToken
if not token then
local return_url = ngx.var.scheme.."://"..ngx.var.host..ngx.var.request_uri
return ngx.redirect("https://sso.yourdomain.com/login?return="..ngx.escape_uri(return_url))
end
-- 验证token有效性
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("https://sso.yourdomain.com/auth/verify", {
method = "GET",
query = {token = token}
})
if not res or res.status ~= 200 then
local return_url = ngx.var.scheme.."://"..ngx.var.host..ngx.var.request_uri
return ngx.redirect("https://sso.yourdomain.com/login?return="..ngx.escape_uri(return_url))
end
}
proxy_pass http://your_backend;
}
五、技术优缺点分析
5.1 优势
- 性能卓越:OpenResty基于Nginx,轻松应对上万并发
- 开发高效:Lua脚本比传统Java开发快得多
- 资源节省:单服务器可同时承担反向代理和认证功能
- 灵活扩展:可以方便集成各种认证方式(LDAP、OAuth等)
5.2 局限性
- 学习曲线:需要同时了解Nginx和Lua
- 调试困难:Lua的错误提示不如Java等语言友好
- 生态局限:某些企业级功能需要自行实现
六、注意事项
- 安全性:一定要使用HTTPS,Cookie设置HttpOnly和Secure标志
- 会话管理:实现完善的token过期和续期机制
- 跨域问题:如果子系统在不同域名,需要考虑CORS方案
- 性能监控:对认证接口要做好监控和限流
- 灾备方案:Redis宕机时的降级处理策略
七、应用场景推荐
这种方案特别适合:
- 企业内部多个系统统一认证
- 微服务架构的API网关层认证
- 需要高并发的Web应用认证
- 快速实现原型验证的场景
不适合:
- 需要复杂权限管理的系统
- 已有成熟IAM系统的企业
- 移动端App的认证场景
八、总结
通过OpenResty实现SSO,我们获得了一个高性能、灵活的解决方案。虽然需要掌握一些Lua和Nginx知识,但投入产出比非常高。对于中小型项目,这可能是最简单快速的SSO实现方案。
关键点回顾:
- 认证中心负责颁发和验证token
- 子系统通过拦截请求实现无感认证
- Redis存储会话状态保证高性能
- 完善的错误处理和跳转逻辑提升用户体验
随着业务发展,可以在此基础上逐步添加多因素认证、风险控制等高级功能,构建更完善的安全体系。
评论