• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >Lua > 使用Lua编写Nginx服务器的认证模块的方法

使用Lua编写Nginx服务器的认证模块的方法

作者:goldensun 字体:[增加 减小] 来源:互联网

goldensun 通过本文主要向大家介绍了nginx lua模块,nginx lua,nginx lua开发,nginx lua redis,lua nginx module等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

 过去两天里,我解决了一个非常有趣的问题。我用一个nginx服务器作为代理,需要能够向其中添加一个认证层,使其能够使用外部的认证源(比如某个web应用)来进行验证,如果用户在外部认证源有账号,就可以在代理里认证通过。
需求一览

我考虑了几种解决方案,罗列如下:

  •     用一个简单的Python/Flask模块来做代理和验证。
  •     一个使用subrequests做验证的nginx模块(nginx目前可以做到这一点)
  •     使用Lua编写一个nginxren认证模块

 

很显然,给整个系统添加额外请求将执行的不是很好,因为这将会增加延迟(特别是给每一个页面文件都增加一个请求是很让人烦恼的).这就意味着我们把subrequest模块排除在外了。Python/Flash解决方案好像对nginx支持的也并不好,所以咱也把它排除了。就剩Lua了,当然nginx对原生化支持得不错的。

因为我不想再扩展的服务器上对每一个请求都做认证,所以我决定生成一些令牌,这样人们就可以将它保存起来,并把它呈现给服务器,然后服务器就让请求通过。然而,因为Lua模块没有一种保持状态的方式(我已经发现),所以我们不能将令牌随处存储。当你没有更多的内存时,怎样来验证用户所说的话呢?

解决问题

加密签名的方式可是咱的救星!我们可以拿用户的用户名和过期时间数据来给用户添加签名的cookies,这样就能很容易的验证每个用户是谁了,同时我们就不用令牌了。

在nginx中,我们要做的就是直接在指定位置配置access_by_lua_file /our/file.lua,这样这个指定位置就可以保护我们的脚本了。现在,让我们一起来写代码:

local cookie = ngx.var.cookie_MyToken
local hmac = ""
local timestamp = ""
local timestamp_time = 0

-- Check that the cookie exists.
if cookie == nil or cookie:find(":") == nil then
    -- Internally rewrite the URL so that we serve
    -- /auth/ if there's no cookie.
    ngx.exec("/auth/")
else
    -- If there's a cookie, split off the HMAC signature
    -- and timestamp.
    local divider = cookie:find(":")
    hmac = cookie:sub(divider+1)
    timestamp = cookie:sub(0, divider-1)
end

-- Verify that the signature is valid.
if hmac_sha1("some very secret string", timestamp) ~= hmac or tonumber(timestamp) < os.time() then
    -- If invalid, send to /auth/ again.
    ngx.exec("/auth/")
end</div>

上面的代码可以直接运行。我们用一些明文来签名(这种情况下用的是一个时间戳,当然你可以用任何你想用的),之后我们用密文生成HMAC(哈希信息认证码),然后一个签名就生成了,这样用户就不能篡改为无效信息了。

当用户试图载入一个资源的时候,我们会检查cookie里面的签名是否有效,如果是,就通过他的请求。反之,我们会把他们重定向到一个发行口令的服务器,这个服务器会验证并且在没有的情况下给予他们一个签名的口令。

明锐的你可能会发现,上面的代码存在时间上的漏洞。如果你没有发现,别难过。嗯,也许会有点难过。

这里是一段Lua的代码,用来比较两个字符串在恒定时间上的等值关系(因而能够阻止任何时间上的攻击,除非我忽视了什么,这极为可能):

    -- Constant-time string comparison function.
    local same = true
    for i = 1, #str1 do
        -- If the two strings' lengths are different, sub()
        -- will just return nil for the remaining length.
        c1 = str1:sub(i,i)
        c2 = str2:sub(i,i)
        if c1 ~= c2 then
            same = false
        end
    end
    return same
end</div>

我已经在函数上应用了时间来区分,如我所知,这是一个在恒定时间下的等值字符串。不同长度的字符串会稍稍改变时间,也许是因为子过程sub应用了一个不同的分支而导致的。而且,c1~=c2分支显然不是恒定时间的,但是在实际中,它相当接近恒定,所以于我们的例子不会有影响。我更倾向于使用XOR操作,从而确定两个字符串的XOR结果是否为0, 不过Lua似乎不包括二进制位的XOR操作。如果我在这个判断上有误,对于任何纠正我都很感激。

口令发行服务器

现在,我们已经写了一些很棒的口令检查代码,所有需要做的,只是写一个服务器来真正的发行这些口令。我本可以用Python以及Flask来写这个服务器,不过我还是想用Go做一个尝试,因为我是一个计算机语言潮人而且Go看上去“酷”。使用Python大概会快一些,不过我乐意用Go。

这个服务器会弹出一个HTTP基础验证的表单,检查你输入的帐户,如果正确,它会给你一个签名的口令,适合于一个小时的代理服务器访问。这样,你只需要验证外部服务一次,而随后的身份验证的检查将在nginx层面,而且会相当的快。

请求处理器

写一个处理器,来弹出一个基本的验证窗体不是很难,但是Go没有完美的文档,所以我必须自己一点点寻猎。其实非常简单,最终,这里就是HTTP基本验证的Go代码:

    if username := checkAuth(r); username == "" {
        w.Header().Set("WWW-Authenticate", `Basic realm="The kingdom of Stavros"`)
        w.WriteHeader(401)
        w.Write([]byte("401 Unauthorized\n"))
    } else {
        fmt.Printf("Authenticated user %v.\n", username)
        token := getToken()
        setTokenCookie(w, token)
        fmt.Fprintf(w, "<html><head><script>location.reload()</script></head></html>")
    }
}</div>

 
设置口令和cookie

一旦我们验证了一个用户之后,我们需要给他们的口令设置一个cookie。我门只需要做我们用Lua做过的同样的事情,如上,只是更加简单,因为Go在标准库里面就包括一个真加密包。这个代码一样很直接明了,即使没有完全文档化:

    expiration := int(time.Now().Unix()) + 3600
    mac := hmac.New(sha1.New, []byte("some very secret string"))
    mac.Write([]byte(fmt.Sprintf("%v", expiration)))
    expectedMAC := fmt.Sprintf("%x", mac.Sum(nil))

    return fmt.Sprintf("%v:%s", expiration, expectedMAC)
}

func setTokenCookie(w http.ResponseWriter, token string) {
    rawCookie := fmt.Sprintf("MyToken=%s", token)
    expire := time.Now().Add(time.Hour)
    cookie := http.Cookie{"MyToken",
        token,
        "/",
        ".example.com",<

您可能想查找下面的文章:

  • lua 基础教程
  • Lua中使用模块的一些基础知识
  • 使用Lua编写Nginx服务器的认证模块的方法
  • 在Lua中使用模块的基础教程
  • 实现Lua中数据类型的源码分享

相关文章

  • Lua中的闭包学习笔记
  • 详解Lua中的数据类型
  • 在Mac OS中安装Lua的教程
  • Lua学习笔记之数据结构
  • Lua基本语法
  • 深入探究Lua中的解析表达式
  • Lua面向对象编程之类的简单实现方式
  • Lua的函数环境、包实例讲解
  • 实现Lua中数据类型的源码分享
  • Lua教程(一):Lua脚本语言介绍

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • Lua中的模块(module)和包(package)详解
    • lua中使用毫秒精度时间的方法
    • Lua极简入门指南(三): loadfile和错误处理
    • Lua编程示例(六): C语言调用Lua函数
    • Lua 操作 MongoDB 数据库实例
    • Lua面向对象编程学习笔记
    • lua日志文件处理代码
    • Lua编程示例(四):Lua标准库之表库、字符串库、系统库
    • Lua cjson模块编译笔记及错误解决方法
    • Lua中的变量和流控制入门学习

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有