- 原理
- 使用同域名(如A.test.com,B.test.com)cookies共享的方式,第一次认证之后,将cookies存放于redis缓存中,每次请求都验证cookies
-
nginx+lua脚本实现
1.nginx 配置
server {
listen 80;
server_name devops.test.com;
error_log /data/logs/nginx/error-devops.log;
location ^~ /proxy/ {
internal; #指定规则为internal规则,防止外部请求命中此规则
rewrite '^/proxy/(http?)/([^/]+)/(\d+)/(.*)' /$4 break;
proxy_pass $1://$2:$3;
}
location @client{
proxy_pass http://10.47.137.120:8001; #devops 服务地址及端口
}
location /api/v1{
proxy_pass http://10.47.137.120:8001; #devops 服务地址及端口
}
location /static{
proxy_pass http://10.47.137.120:8001;
}
location / {
access_by_lua_file 'conf/vhosts/lua/devops.lua';
}
}
1.从nginx的配置文件中可以看出,/api/v1/ 和/static 是没有调用lua脚本,而请求/ 都要走lua脚本认证- @client 请求是lua脚本中指定的uri
-
/proxy/ 是lua脚本中使用的uri,主要是用来跳转到认证登录平台
2.lua 脚本实现 devops.lua
local sso = 'http://sso.test.com'
local mysite = 'http://devops.test.com/'
local login_url = sso .. '?next=' .. mysitefunction Logout()
cookie_key = ngx.var.cookie_cookie_key
if cookie_key ~= nil then
local check_url = 'http/100.114.64.185/8000/logout/'
local checkSign = 'cookie_key='.. cookie_key
checkUrl = '/proxy/' .. check_url .. '?' .. checkSign
local res = ngx.location.capture(checkUrl, {
method = ngx.HTTP_GET,
})
ngx.redirect(login_url)
else
ngx.redirect(login_url)
end
end
function getSSOUser()
cookie_key = ngx.var.cookie_cookie_key
if cookie_key == nil then
ngx.redirect(login_url)
end
-- check user cookie
local check_url = 'http/100.114.64.185/8000/sso/' -- format used by ngx.location.capture && proxy_pass(a httpclient simulation)
local checkSign = 'cookie_key='.. cookie_key..'&&url='..mysite
checkUrl = '/proxy/' .. check_url .. '?' .. checkSign
--ngx.say(checkUrl)
local res = ngx.location.capture(checkUrl, {
method = ngx.HTTP_GET,
})
local cjson = require("cjson")
--ngx.say(res.body)
if 'false' == res.body then
ngx.redirect(login_url)
--ngx.say(res.body)
else
obj = cjson.decode(res.body)
user = obj['user']
return user
end
end
function doLogin()
local user = getSSOUser()
ngx.header['Set-Cookie'] = {'x_webauth_user =' .. user}
ngx.req.set_header("USER", user)br/>ngx.exec("@client")
end
if ngx.re.match(ngx.var.request_uri, "logout") then
ngx.header['Set-Cookie'] = {'x_webauth_user = false'}
Logout()
else
local x_user = ngx.var.cookie_x_webauth_user
local cookie_key = ngx.var.cookie_cookie_key
if cookie_key then
doLogin()
else
ngx.redirect(login_url)
end
end - lua脚本看起来没那么难,相对比较好理解
-
最前面的三个local 是定义变量
if ngx.re.match(ngx.var.request_uri, "logout") then
ngx.header['Set-Cookie'] = {'x_webauth_user = false'}
Logout()
#判断请求的uri里面是否有“logout”,匹配则设置cookies的 x_webauth_user值
为false,并调用logout函数
3.如果uri不包括logout
local cookie_key = ngx.var.cookie_cookie_key 获取cookies下cookie_key的值
如果值为空,直接从定向到单点登录,login_url为
http://sso.test.com?next=http://devops.test.com/ ,单点登录平台需要做的
是获取next后面的uri及认证,认证成功之后,redis缓存cookie_key,
将cookie_key信息写入cookies中,让下次请求的时候带上cookie_key
4.如果cookie_key值存在,则调用认证函数doLogin()
function doLogin()
local user = getSSOUser() #调用getSSOUser 函数
ngx.header['Set-Cookie'] = {'x_webauth_user =' .. user}
ngx.req.set_header("USER", user)
ngx.exec("@client") #匹配nginx @client url
endfunction getSSOUser() cookie_key = ngx.var.cookie_cookie_key if cookie_key == nil then ngx.redirect(login_url) end -- check user cookie local check_url = 'http/100.114.64.185/8000/sso/' -- format used by ngx.location.capture && proxy_pass(a httpclient simulation) local checkSign = 'cookie_key='.. cookie_key..'&&url='..mysite checkUrl = '/proxy/' .. check_url .. '?' .. checkSign --ngx.say(checkUrl) local res = ngx.location.capture(checkUrl, { method = ngx.HTTP_GET, }) #get请求单点登录平台的接口/sso/cookie_key=* 验证cookie_key 的有效性,错误返回false local cjson = require("cjson") --ngx.say(res.body) if 'false' == res.body then #如果返回false,重新跳回登录界面 ngx.redirect(login_url) --ngx.say(res.body) else obj = cjson.decode(res.body) user = obj['user'] return user #正确则返回认证用户及cookie信息写入 end
end
5.注销函数
function Logout()
cookie_key = ngx.var.cookie_cookie_key #获取cookie_key 值
if cookie_key ~= nil then #如果值不为空
local check_url = 'http/100.114.64.185/8000/logout/'
local checkSign = 'cookie_key='.. cookie_key
checkUrl = '/proxy/' .. check_url .. '?' .. checkSign
local res = ngx.location.capture(checkUrl, {
method = ngx.HTTP_GET,
}) #请求http://100.114.64.185:8000/logout/?cookie_key=****,通过get请求删除redis中的cookie值,达到注销的功能
ngx.redirect(login_url)
else
ngx.redirect(login_url)
end
end
6.http://100.114.64.185:8000 是一个阿里云内网负载均衡,后端是两台单点认证服务
- 登录平台
1.可以用django写一个认证平台,也可以用flask写,个人推荐flask,轻量级
2.平台需要的功能
认证用户名及密码(对接LDAP)
提供cookie值判断接口API
删除cookie值的接口API
3.django实现,部分代码
redis 连接:
def rds(key='',name='',status=''):
import redis
Rds = rdsconfig()
host = Rds['host']
port = Rds['port']
passwd = Rds['passwd']
if passwd:
r = redis.Redis(host=host,port=port,password=passwd)
else:
r = redis.Redis(host=host,port=port)
if status == 'add':
r.setex(key,name,21600)
elif status == 'get':
return r.get(key)
elif status == 'del':
return r.delete(key)
登录部分:
def login(request):
cip=get_client_ip(request)
cap_form = CaptchaLoginForm()
#print cap_form
if request.method == 'GET':
#cap_form = CaptchaLoginForm()
try:
url=request.get_full_path()
url=url.split('=')[1]
logger.info(color_print('{} - access index'.format(cip),'info'))
except:
return render(request,'login.html',locals())
return render(request,'login.html',locals())
elif request.method == 'POST':
username = request.POST.get('username').strip(' ')
url = request.POST.get('url').strip(' ')
response=HttpResponseRedirect(url)
secret=MkPasswd.Passwd() #生成随机数
secret=MkPasswd.Passwd()+username #md5 加密随机数,生成cookie_key
secret_key=md5Encode(secret)
rds(secret_key,username,'add')
response.set_cookie('cookie_key',secret_key,\
max_age=606012,domain='.yongqianbao.com')
#response.set_cookie('x_webauth_user',username)
if url == '/':
return HttpResponseRedirect('http://intra.yongqianbao.com')
return response
else:
return render(request,'login.html',locals())
判断cookie_key 是否存在于redis中
def sso(request):
import json
cip=get_client_ip(request)
if request.method == 'GET':
cookie_key=request.GET.get('cookie_key')
url=request.GET.get('url')
#print url
#print cookie_key
time_now = int(time.time())
user=rds(key=cookie_key,status='get')
if user:
watch={'user':user}
login=Login(username=user,url=url,ip=cip,time=time_now,status=1)
login.save()
response=HttpResponse(json.dumps(watch),content_type="application/json")
return response
else:
return HttpResponse('false')
注销函数:
def logout(request):
if request.method == 'GET':
cookie_key=request.GET.get('cookie_key')
#url=request.GET.get('url')
is_succ=rds(key=cookie_key,status='del')
#print is_succ
return HttpResponse(True) - 作品展示