之前使用了openresty进行了rsa跟aes的加解密测试。现在我们整合一下、使用openresty连接redis做鉴权、解密。之前提到过,我们不使用cookie而是使用token来认证用户信息。而且token是我们自己加密的、加密的规则就是使用aes进行加密。我们再来缕一缕整个流程。

客户端(浏览器)流程:

        第一步:先获取token(临时token),返回的token是aes加密的,这里的密钥我们就使用固定的aes密钥就好了。token里面包含userId跟tokenId,我们使用tokenId来做我们缓存的key;

        第二步:获取rsa公钥,这里必须带上token才能去获取rsa密钥对;

        第三步:客户端(浏览器)每进入一个页面(或者每次请求)随机生成aes密钥,使用该密钥对请求体加密;

        第四步:客户端(浏览器)在发送请求之前,将随机生成的aes密钥使用rsa公钥加密,并写入当前请求的请求头中;

openresty鉴权+解密流程

        第一步:获取请求token,并对token进行解密,使用aes与java后端约定好的密钥;

        第二步:从redis(存储之前的文章有介绍)获取用户信息,做登录认证,循环角色编码+uri做鉴权;

        第三步:从redis中获取与客户端对应的私钥,解密客户端随机生成的aes密钥;

        第四步:使用客户端的aes密钥解密客户端请求体内容;

代码

        大致的流程就是这样了、接下来我们看看实现的代码跟测试的效果。

redisUtil.lua(需要放在lualib\resty目录下)

local U_ = {_VERSION='1.0.0'}
function U_.keepalive(redis)
	redis:set_keepalive(1000, 200)
end
function U_.connectRedis(redis)
	redis:set_timeouts(1000, 1000, 1000)
	local ok, err = redis:connect("127.0.0.1", 6379)
	if not ok then
		ngx.exit(601)
		return
	end
	-- 设置密码
	local ok, err = redis:auth("redis密码")
	if not ok then
		ngx.exit(602)
		return
	end
end
function U_.getRedisKey(redis,key)
	local res, err = redis:get(key)
	if not res then
		return ngx.null
	end
	return res
end
return U_

nginx配置

server {
	listen 6378;
    location /redis {
		default_type text/html;
		content_by_lua_file lua/gateway.lua;
	}
}

gateway.lua

local nowUri = string.gsub(ngx.var.uri, "/", "-")
ngx.say("请求的URI:",nowUri)
local headers = ngx.req.get_headers()
local token = headers["token"]
if token == nil or token == ngx.null or token == '' then 
	--ngx.exit(403)
	ngx.say("获取不到token信息")
	return
end
-- AES使用固定的key跟向量解密token
local key = "569d874ds63219ds"
local iv = "dsfd985s6d7yfd54"
local aes = require "resty.aes"
local aes_128_cbc_with_iv = assert(aes:new(key,nil, aes.cipher(128,"cbc"), {iv=iv}))
local cipherBytes = ngx.decode_base64(token)
local tokenText, err = aes_128_cbc_with_iv:decrypt(cipherBytes)
ngx.say("token解密结果:",tokenText)
if not tokenText then
	--ngx.exit(403)
	ngx.say("token解密失败:",err)
	return
end
if tokenText == nil or tokenText == ngx.null or tokenText == '' then
	--ngx.exit(403)
	ngx.say("token解密明文为空:",token)
	return
end
local userTokenKey = string.sub(tokenText,38,#tokenText)

-- 连接redis
local redisClient = require "resty.redis"
local redis = redisClient:new()
local redisUtil = require "resty.redisUtil"
redisUtil.connectRedis(redis)

-- 获取用户角色信息——认证
local userInfo = redisUtil.getRedisKey(redis,'user-info:'..userTokenKey)
ngx.say("用户角色:",userInfo)
if userInfo == nil or userInfo == ngx.null then
	-- 读取不到用户信息
	--ngx.exit(403)
	ngx.say("读取不到用户信息:",token)
	return
end

local json = require("cjson")
local userInfoJson = json.decode(userInfo)
local roleList = userInfoJson.role
local authFlag = false
-- 循环匹配角色+uri鉴权
for i, v in ipairs(roleList) do
	local uriAuth = redisUtil.getRedisKey(redis,'auth-url:'.. v.roleCode .. ':' .. nowUri)
	if uriAuth ~= nil and uriAuth ~= ngx.null and tonumber(uriAuth) == 1 then
		authFlag = true
		break
	end
end

if authFlag == false then
	-- 没有权限
	ngx.say("没有权限:",token)
	--ngx.exit(403)
	return
end

-- 截取并拼接出redis存储的rsa私钥的key(注意不要有中文)
local redis_priv_key = 'rsa-key:' .. userTokenKey
ngx.say("rsa_priv_key:",redis_priv_key)
local rsa_priv_key = '-----BEGIN PRIVATE KEY-----\n' .. redisUtil.getRedisKey(redis,redis_priv_key) .. '\n-----END PRIVATE KEY-----'
-- 关闭redis连接
redisUtil.keepalive(redis)
ngx.say(rsa_priv_key)

-- RSA解密:客户端AES密钥
local resty_rsa = require "resty.rsa"
local service_priv, service_err = resty_rsa:new({ 
	private_key = rsa_priv_key ,
	key_type = resty_rsa.KEY_TYPE.PKCS8,
	padding = resty_rsa.PADDING.RSA_PKCS1_OAEP_PADDING
})
if not service_priv then
	ngx.say("创建私钥错误: ", service_err)
	--ngx.exit(500)
	return
end
local pub_pass_str = headers["passkeys"]
ngx.say("客户端aes密钥密文: ", pub_pass_str)
-- 使用java生成的私钥、解密客户端使用公钥加密的密文
local service_priv_de = service_priv:decrypt(ngx.decode_base64(pub_pass_str))
ngx.say("客户端aes密钥明文: ", service_priv_de)
local client_aes_key = string.sub(service_priv_de,0,16)
local client_aes_iv = string.sub(service_priv_de,18,33)
ngx.say("客户端aes key: ", client_aes_key)
ngx.say("客户端aes iv: ", client_aes_iv)
local aes_cbc_client = assert(aes:new(client_aes_key,nil, aes.cipher(128,"cbc"), {iv=client_aes_iv}))
ngx.req.read_body()
local req_body = ngx.req.get_body_data()
ngx.say(req_body)

local req_body_json = json.decode(req_body);
local de_body_text = ngx.decode_base64(req_body_json.text)
local body_text, _body_err = aes_cbc_client:decrypt(de_body_text)
ngx.say("body解密结果:",body_text)

redis数据

        用户

        私钥 (跟之前加密篇的一致)

         权限(固定前缀:角色编码:uri——uri中的/被替换成-)

 

测试

        使用postman发送测试请求

        请求头:

token:PGpgSf1e6ZfD8tzTnHUr4WO8JCNi6GeZE+WP9FfA/rdZRAeFp1Kan8FOVrf0D9FHLIQpMGfKY075EEichxlytedD4Y2zVE7xRJQL0ejnxj4=
passkeys:Z7W7qJhA+rj/qxDkT3Yr3u6GkoAg88q9Ct5IurQUdwqEJGrBx172Pez8js1mnpekiFljdkhb96+JyKi8odhIXkNGEFMcSYoLBFxyQK3wzcpdzm1RXEvmSHmGLsgMTebWlIFiZnfC7PnKaxS3EF/omHmYt9IQBEqN6XAHVE6IB9K4TPGvoraPGbzJqdMr4D6CYZ2KsKKgdyjBOUJMN9UrKCvcuruXwknOXV4i8R46XwM9g8YcAH8Hfmujo07YDVicxy5dWTS3/M56zzSbeTIv67lENeiSSygtKAcrIZF+dRM6NAULckoyPAsD7SWX68AGql4dxS1fQw7EpWZTRvkaMA==

        请求体:

{

    "text":"iyAuevH2ik9Ld6JxJDXYw0vxj0s4YL9EtqQ8OzsZmY+U6ggQ4kWxTlVDc/rpSefYBFO/gK+epvF/nWQsh6HTRNxpnDbwFwiEXFXLIuXFjHo="

}

        结果:

请求的URI:-redis-test
token解密结果:12d57406-28aa-41f5-818d-6521e44949bd@e086a63d-196c-4b3e-9aa4-768f7d51cf52
用户角色:{"userName":"administrator","age":18,"sex":"男","idCard":"668","role":[{"roleName":"超级管理员","roleCode":"root"},{"roleName":"测试","roleCode":"test"},{"roleName":"普通管理员","roleCode":"admin"}]}
rsa_priv_key:rsa-key:e086a63d-196c-4b3e-9aa4-768f7d51cf52
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCM56VH633AAvZ
/SzfFkByba6fVx6ClCx1KZe90PQyZ8uSuxUBdmXZBzdXg/R1u2h6GqhCT7Uvnxu
UN+xxPnJbCn6DtM/5tDA3IJV/aWA67eQr9lZBvvjrLeBu3huAC0EldojX0FQBnV
2tPTHiJpMDkWhLgDeGVVUZTQfWhxVvnAuAxzeiwi/pfGs5wzCI0kzF8q8xGjo83
7hv5ccAmdSNenWal+c4XcKTkiWZyExAAnmdxEL0NQSSvg1cHa4NPfqRJTqfFFHy
/1TlA7jE1GNga2AUc/DDrDfRl4c0qI6nb9xOkh1KNqEkJ8S5cneG04ds7z+IzIr
eWw8dnCsghh2vBAgMBAAECggEASH1T9rAcPZBIqCxWQPlm/j5gVgchikcxhnjyu
+Y8eWcQZylrd7vfrvLqOZl+bu0gDz+mz7Og/VjBtnOdmQeCOBZPgDjjh85PuMwh
h/8NdT1MfjFX4WUIcm0UNVLaJBhr5hPxleTIFGJQ/rbkvEtaQSBl3YbGq0D3sRW
Z/OO/6BQ7PhVWMsqexAbgKQU3MhCoZoURovffdbxhNJQiOR2oQnf31+4MV6f8QV
2UTCvhsb2Z8ybXMCJGUssRkSud2rkOB+T1/vqDOADEdo6Srozlq4iQ7FgbyApsF
9G1ZEjF8L0AC+9eCZSivYvTJceqO9/KHSJtBhnm7hFz2EMVi7ZPwQKBgQC/ScXb
lSuMASwGuWnU2kWIrsy3h7R/PqfeN4AjeIWZGOIkjrQQ0l+y1HjRW3aJ5yWqtsP
Yf2y+t/+7h3tLZ8AS4UrJTgRV3p/cBOD6GGfjOfdAxCpcaofwwl8ZA4CXMUxbbJ
UogQHnmAwfBPlkH2YUcfpS+ZuuZVsHYgBD8rsNPQKBgQC8koBZlLv9JyFgmz/rk
ABBJy6u4Th/spn7qYuiWXGMo/EPAdDeTQVf750/8bV5kv4hZysCz8j9isYtvGYR
CuRvcaaGBjMIJ7sV7YTHH9phoon+Gzf/kUWQslFupWu/9Dv60Nb3iOD26oKfBFf
NhlfczPVXhQnttQkgZ8GM+CGI1QKBgBkcOw/fHg9L3Bap4j2hxXzyzUbOVqBZfj
nKeVSuroLxZEY+QV7v7sYP5Cg/ZGkn4abuRPk3iPPkPXrFhybX4LvZvTJ9vk3zY
nLEZTAPYhvO8SkcVx84kM3HBirHberq+sYJk+70OGbJa9Xqlj5RbNoEOEMKJyiW
f4ORls1UoL9VAoGAHIF8+427WUp4BjWR1RdAopi8utz7AHrMQjngDNu+iYci4qT
goSo9fMIpIEh2qXkqB3ykCNnGRWWcDb/kIgFmhN5GUQ5Q2pO++VKddsh+57F9cL
dGoNCiFnyOSM6i2jKeeozlYigD8e+DbWxnpX8AezVUhTVsSc3LImXs4VWFJD0Cg
YBQPpWmlcij6zD1Qdgb6X6YcZBI/vfwQO8LoDerr6AwJImwHswHz7gENCeT99ZN
IORX+wKRutMHSLiIFIw3XGpkg1cp3m4hHvdDsLIGIWZaIQBDEMOOdRsDGKDBl9R
GpwEUgsRssMuVjOHRijicIA86yu3wzP6sO+7ttHCnBO8Aig==
-----END PRIVATE KEY-----
客户端aes密钥密文:
Z7W7qJhA+rj/qxDkT3Yr3u6GkoAg88q9Ct5IurQUdwqEJGrBx172Pez8js1mnpekiFljdkhb96+JyKi8odhIXkNGEFMcSYoLBFxyQK3wzcpdzm1RXEvmSHmGLsgMTebWlIFiZnfC7PnKaxS3EF/omHmYt9IQBEqN6XAHVE6IB9K4TPGvoraPGbzJqdMr4D6CYZ2KsKKgdyjBOUJMN9UrKCvcuruXwknOXV4i8R46XwM9g8YcAH8Hfmujo07YDVicxy5dWTS3/M56zzSbeTIv67lENeiSSygtKAcrIZF+dRM6NAULckoyPAsD7SWX68AGql4dxS1fQw7EpWZTRvkaMA==
客户端aes密钥明文: 6zUwU5McVrZdkKMs@vpgmUFeRd9Ms5o7G
客户端aes key: 6zUwU5McVrZdkKMs
客户端aes iv: vpgmUFeRd9Ms5o7G
{
"text":"iyAuevH2ik9Ld6JxJDXYw0vxj0s4YL9EtqQ8OzsZmY+U6ggQ4kWxTlVDc/rpSefYBFO/gK+epvF/nWQsh6HTRNxpnDbwFwiEXFXLIuXFjHo="
}
body解密结果:{"userName":"administrator","passWord":"Xts@#!$8965...dTT","type":"pass"}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐