Nginx鉴权功能实现

背景

鉴权的功能是防止盗链,别人一直访问你的连接,把你的服务器打爆,当鉴权失败时(md5值计算错误、时间戳过期),nginx直接返回403。
鉴权主要分为四种:

  • 时间戳鉴权
  • 远程鉴权
  • Referer黑白名单
  • IP黑白名单

在使用上,一般推荐时间戳鉴权和远程鉴权,或者两者同时使用,剩下两项规避鉴权非常容易。

时间戳鉴权介绍(LSS的方式)

为确保视资源被非法获取,提供token认证和有效期限相结合的播放地址。开启时间戳鉴权后,通过”播放地址+timestamp+secret”的方式获取完整的加密播放地址。

地址如下:

https://domain.com/live/123.m3u8?timestamp=1680709499&secret=af921173a6c64469f31fbba2fa7e7110

算secret,方式为:md5(密钥+播放地址+timestamp)

  • 密钥:在播放认证开关可以获取到播放密钥,按照步骤一已获取。

  • 播放地址:/<play.your-domain.com>/{app-name}/{stream-name}

  • timestamp:用户指定播放的超时时间,格式需要转换为十进制 Unix 时间戳,推荐一个在线转换网址

  • 将(密钥+播放地址+timestamp)拼接完成后,加密为32位小写的md5码。

    假设key=111, domain = play.domain.com, app=live, stream=123, timestamp=1680709499,md5加密后的secret为:md5(111/domain.com/live/1680709499)=秘钥

中心鉴权介绍

中心鉴权是指鉴权由用户测规定,用户请求到nginx后,nginx会发送一个请求到鉴权服务器,由鉴权服务器返回的状态码来判断这个请求鉴权是否通过。

​ 中心鉴权没有规范可言,基本都是定制化。

功能实现

通过lua实现的逻辑如下:

  1. 修改nginx.conf 主配置文件,增加rewrite_by_lua_file ,指定改写脚本
http {
  access_by_lua_file lua/access/auth.lua;
}
  1. 编写处理逻辑
local cjson = require "cjson"
local ssl = require "ngx.ssl"
local http = require('resty.http')

local globalconfiguration = require "configuration"
local tool = require "tool"
local dy_conf = ngx.shared.conf

local function isintable(value,tb)
    for k,v in pairs(tb) do
        if v == value then
            return true
        end
    end
    return false    --重点:全部跑完以后,如果非true,则返回false
end

--远程鉴权使用函数,不使用
local function center_auth()
    --获取鉴权地址
    local auth_server = globalconfiguration.chinaunicom_center_auth	--从配置文件中读取远程鉴权接口
    --获取客户端IP
    local remore_addr = ngx.var.remote_addr
    -- 获取uri
    local uri = ngx.var.uri
    -- 获取域名
    local headers = ngx.req.get_headers()
    local host = headers["Host"]
    --获取对应的id
    local auth_field = "uid"
    local args = ngx.req.get_uri_args()
    local uid = args[auth_field]
    if not uid then
        ngx.log(ngx.ERR, "client not take cneter auth  field: ".. auth_field)
        ngx.exit(403)
    end


    ngx.log(ngx.INFO, "[debug]auth_serverL: " .. auth_server, "; remore_addr: "..remore_addr .. "; uri:".. uri, ";Host: " .. host)
    local httpc = http.new()
    local url = "http://" .. auth_server.. "/kuanshijie/center_auth?uid=".. uid .."&client_ip="..remore_addr.."&client_uri=" .. uri .. "&client_host=" .. host
    ngx.log(ngx.ERR, "uri: ".. url)
    httpc:set_timeout(500)
    local res, err = httpc:request_uri(url, {
        keepalive_timeout = 500 -- 毫秒
    })
    ngx.log(ngx.INFO, "center auth status : ".. res.status)
  	if not res then		--超时放行
    	  return 
    end
    if  res.status == 403 then
        ngx.log(ngx.ERR, "center auth response 403")
        ngx.exit(403)
    end
end
--请求接口

local function lss_auth(host,time_key)
    --获取参数
    local args = ngx.req.get_uri_args()
    local client_host = args["client_host"]
    --local client_uri = ngx.var.uri
    local uri = ngx.var.request_uri
    local client_uri = tool.split(uri,"?")[1]
    local timestamp = args["timestamp"]
    if timestamp == nil then
        ngx.log(ngx.ERR, "ERROR timestamp is nil")
        ngx.exit(403)
    end
    local md5 = args["secret"]
    local timestamp_key = time_key
    local now = ngx.time()
    ngx.log(ngx.ERR, "[debug]now: ".. now .."; timestamp: ".. timestamp)
    if now > tonumber(timestamp) then
        ngx.log(ngx.ERR, "ERROR req is expired! host: ", host, " uri: ", uri)
        ngx.exit(403)
    end
    local idx = string.find(client_uri, ".m3u8")
    local uri_tmp = string.sub(client_uri, 1, idx - 1)
    local str = string.format("%s/%s%s%s", timestamp_key, host, uri_tmp, timestamp)
    local val = ngx.md5(str)
    if val ~= md5 then
        ngx.log(ngx.ERR, "ERROR req md5 is error! secret: ", md5, " val: ", val, " str: ", str)
        ngx.exit(403)
    end
    ngx.log(ngx.INFO, "DEBUG timestamp auth success! val: ", val, " timestamp: ", timestamp)

end


local function process()
    local host = ssl.server_name()
    local str = dy_conf:get(host)	--从共享内存中读取域名配置
    local conf = cjson.decode(str)
    local auth = conf["conf"]["m3u8_auth_list"]	--判断域名配置项
    if auth then
        -- 判断是否存在kuanshijie_center_auth
        if isintable("center_auth",auth) then
            --center_auth()
        end
        if isintable("lss_auth", auth) then
            local time_key = conf["conf"]["cucc_anti_md5"]	--读取key
            ngx.log(ngx.ERR, "[debug] is match lss_auth")
           lss_auth(host,time_key)
        end
    end
end
process()

以上代码块有编写时间戳鉴权和中心鉴权两个功能,案例只使用了时间戳鉴权。

posted @ 2023-04-05 20:00  better_feng  阅读(2298)  评论(0编辑  收藏  举报