摘要:用于方便地搭建能夠處理超高并發(fā)擴(kuò)展性極高的動態(tài)應(yīng)用服務(wù)和動態(tài)網(wǎng)關(guān)。
介紹
權(quán)限認(rèn)證是接口開發(fā)中不可避免的問題,權(quán)限認(rèn)證包括兩個方面
接口需要知道調(diào)用的用戶是誰
接口需要知道該用戶是否有權(quán)限調(diào)用
第1個問題偏向于架構(gòu),第2個問題更偏向于業(yè)務(wù),因此考慮在架構(gòu)層解決第1個問題,以達(dá)到以下目的
所有請求被保護(hù)的接口保證是合法的(已經(jīng)認(rèn)證過的用戶)
接口可以從請求頭中獲取當(dāng)前用戶信息
每個請求都有uuid用于標(biāo)識
JWT(JSON Web Token)目前是應(yīng)用最廣的接口權(quán)限方案,具有無狀態(tài),跨系統(tǒng),多語言多平臺支持等特點,如果能在網(wǎng)關(guān)層實現(xiàn)JWT驗證不僅可以避免代碼入侵還能為整個后臺提供統(tǒng)一的解決方案,目前客戶網(wǎng)關(guān)使用Nginx,但社區(qū)版Nginx中沒有JWT模塊,自己實現(xiàn)不現(xiàn)實,因此選擇OpenResty作為網(wǎng)關(guān)層, 據(jù)官網(wǎng)介紹,OpenResty? 是一個基于 Nginx 與 Lua 的高性能 Web 平臺,其內(nèi)部集成了大量精良的 Lua 庫、第三方模塊以及大多數(shù)的依賴項。用于方便地搭建能夠處理超高并發(fā)、擴(kuò)展性極高的動態(tài) Web 應(yīng)用、Web 服務(wù)和動態(tài)網(wǎng)關(guān)。本質(zhì)上就是一個Nginx+Lua的集成軟件.
整體架構(gòu)如圖:
[root@docker ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) [root@docker ~]# more /proc/version Linux version 3.10.0-693.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4. 8.5-16) (GCC) ) #1 SMP Tue Aug 22 21:09:27 UTC 2017安裝OpenResty
OpenRestry安裝很簡單,可以在這里找到不同版本操作系統(tǒng)安裝文檔,本次使用的環(huán)境是CentOS Linux release 7.4
[root@docker ~]# yum install yum-utils [root@docker ~]# yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo [root@docker ~]# yum install openresty [root@docker ~]# yum install openresty-resty
系統(tǒng)默認(rèn)安裝在/usr/local/openresty/目錄下,版本如下
[root@docker openresty]# cd /usr/local/openresty/bin/ [root@docker bin]# ./openresty -v nginx version: openresty/1.13.6.2
可以將OpenResty目錄加到PATH里,方便使用.
修改nginx.conf文件測試是否安裝成功
tee /usr/local/openresty/nginx/conf/nginx.conf <<-"EOF" worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location / { default_type text/html; content_by_lua " ngx.say("安裝JWT模塊hello, world
") "; } } } EOF [root@docker conf]# openresty -s reload [root@docker conf]# curl localhost:8080hello, world
這里使用JWT官方推薦Lua實現(xiàn)庫,項目地址為https://github.com/SkyLothar/...,有趣的是,這個項目的介紹是這么寫的JWT For The Great Openresty,看來是為OpenResty量身定做.github上有安裝教程,但一方面有些第三方庫的安裝文檔沒有提及,另一方面有些內(nèi)容沒有用到安裝的時候可以跳過,這里將完整安裝步驟重新整理了下.
在release頁面https://github.com/SkyLothar/...下載項目源碼,截止到目前最新版為v0.1.11
下載hmac源碼,截止到目前項目還未release,只能下載項目里的源文件https://github.com/jkeys089/l...
在服務(wù)器創(chuàng)建目錄/usr/local/openresty/nginx/jwt-lua/resty,將第1步壓縮包中目錄lua-resty-jwt-0.1.11/lib/resty/下的所有l(wèi)ua文件和第2步中的hmac.lua文件拷貝到該目錄下,文件列表如下.
[root@docker resty]# pwd /usr/local/openresty/nginx/jwt-lua/resty [root@docker resty]# ll total 60 -rwxr-xr-x. 1 root root 11592 Jul 18 10:40 evp.lua -rw-r--r--. 1 root root 3796 Jul 18 10:40 hmac.lua -rwxr-xr-x. 1 root root 27222 Jul 18 10:40 jwt.lua -rwxr-xr-x. 1 root root 15257 Jul 18 10:40 jwt-validators.lua
修改nginx.conf驗證是否生效
tee /usr/local/openresty/nginx/conf/nginx.conf <<-"EOF" worker_processes 1; error_log logs/error.log info; events { worker_connections 1024; } http { lua_package_path "/usr/local/openresty/nginx/jwt-lua/?.lua;;"; server { listen 8080; default_type text/plain; location = / { content_by_lua " local cjson = require "cjson" local jwt = require "resty.jwt" local jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" .. ".eyJmb28iOiJiYXIifQ" .. ".VAoRL1IU0nOguxURF2ZcKR0SGKE1gCbqwyh8u2MLAyY" local jwt_obj = jwt:verify("lua-resty-jwt", jwt_token) ngx.say(cjson.encode(jwt_obj)) "; } location = /sign { content_by_lua " local cjson = require "cjson" local jwt = require "resty.jwt" local jwt_token = jwt:sign( "lua-resty-jwt", { header={typ="JWT", alg="HS256"}, payload={foo="bar"} } ) ngx.say(jwt_token) "; } } } EOF
[root@docker resty]# curl localhost:8080 {"signature":"VAoRL1IU0nOguxURF2ZcKR0SGKE1gCbqwyh8u2MLAyY","reason":"everything is awesome~ :p","valid":true,"raw_header":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9","payload":{"foo":"bar"},"header":{"alg":"HS256","typ":"JWT"},"verified":true,"raw_payload":"eyJmb28iOiJiYXIifQ"}
驗證通過,jwt模塊安裝完畢
自定義驗證邏輯上面jwt模塊還無法用于生產(chǎn)環(huán)境,有幾個問題沒解決
jwt token目前是寫死在配置文件里,生產(chǎn)需要從header Authorization中獲取
驗證失敗目前返回是200生產(chǎn)需要返回401
需要配置反向代理并且將用戶信息放在代理header上
創(chuàng)建文件/usr/local/openresty/nginx/jwt-lua/resty/nginx-jwt.lualocal jwt = require "resty.jwt" local cjson = require "cjson" --your secret local secret = "5pil6aOO5YaN576O5Lmf5q+U5LiN5LiK5bCP6ZuF55qE56yR" local M = {} function M.auth(claim_specs) -- require Authorization request header local auth_header = ngx.var.http_Authorization if auth_header == nil then ngx.log(ngx.WARN, "No Authorization header") ngx.exit(ngx.HTTP_UNAUTHORIZED) end ngx.log(ngx.INFO, "Authorization: " .. auth_header) -- require Bearer token local _, _, token = string.find(auth_header, "Bearer%s+(.+)") if token == nil then ngx.log(ngx.WARN, "Missing token") ngx.exit(ngx.HTTP_UNAUTHORIZED) end ngx.log(ngx.INFO, "Token: " .. token) local jwt_obj = jwt:verify(secret, token) if jwt_obj.verified == false then ngx.log(ngx.WARN, "Invalid token: ".. jwt_obj.reason) ngx.exit(ngx.HTTP_UNAUTHORIZED) end ngx.log(ngx.INFO, "JWT: " .. cjson.encode(jwt_obj)) -- write the uid variable ngx.var.uid = jwt_obj.payload.sub end return M修改配置文件nginx.conf
worker_processes 1; error_log logs/error.log info; events { worker_connections 1024; } http { upstream tomcat{ server localhost:80; } lua_package_path "/usr/local/openresty/nginx/jwt-lua/?.lua;;"; server { listen 8080; set $uid ""; location / { access_by_lua " local jwt = require("resty.nginx-jwt") jwt.auth() "; default_type application/json; proxy_set_header uid $uid; proxy_pass http://tomcat; } } }
這里后臺啟動了一臺tomcat并設(shè)置監(jiān)聽端口為80,tomcat上部署了一個示例的war包,代碼邏輯較簡單,就是輸出所有的header,代碼如下:
package asan.demo; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.*; import javax.servlet.http.*; public class JWTDemoService extends HttpServlet { private static final String CONTENT_TYPE = "text/html; charset=UTF-8"; public void init(ServletConfig config) throws ServletException { super.init(config); } public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(CONTENT_TYPE); PrintWriter out = response.getWriter(); Enumeration em=request.getHeaderNames(); while(em.hasMoreElements()){ String key=(String)em.nextElement(); String value=(String)request.getHeaders(key).nextElement(); out.println(String.format("%s ==> %s", key,value)); } out.close(); } }
重啟OpenResty測試,如果沒有指定jwt token信息返回401
[root@docker conf]# curl http://localhost:8080/jwtdemo/service401 Authorization Required 401 Authorization Required
openresty/1.13.6.2
指定jwt token
[root@docker conf]# curl -i http://localhost:8080/jwtdemo/ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ5YXlhIiwiaWF0IjoxNTMxODkyNzE3LCJpc3MiOiJ5YXlhIiwic3ViIjoieWF5YSIsImV4cCI6MTUzMTkyODcxN30.W5UXlwKHSrpUAYbfoF-fTBTS9Enm1wsvCKNQm0yLSfQ" HTTP/1.1 200 Server: openresty/1.13.6.2 Date: Wed, 18 Jul 2018 05:52:13 GMT Content-Type: text/html;charset=UTF-8 Content-Length: 298 Connection: keep-alive uid ==> yaya host ==> tomcat connection ==> close user-agent ==> curl/7.29.0 accept ==> */* authorization ==> Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ5YXlhIiwiaWF0IjoxNTMxODkyNzE3LCJpc3MiOiJ5YXlhIiwic3ViIjoieWF5YSIsImV4cCI6MTUzMTkyODcxN30.W5UXlwKHSrpUAYbfoF-fTBTS9Enm1wsvCKNQm0yLSfQ
從結(jié)果上看,后臺服務(wù)已經(jīng)獲取到uid這個header
至于請求用到j(luò)wt token可以從任意平臺生成只要保證secret一樣即可,根據(jù)官網(wǎng)介紹,該庫目前支持到j(luò)wt生成算法如圖:
為每個請求生成唯一的uuid碼可以將網(wǎng)關(guān)層上的請求和應(yīng)用層的請求關(guān)聯(lián)起來,對排查問題,接口統(tǒng)計都非常有用.
local M = {} local charset = {} do -- [0-9a-zA-Z] for c = 48, 57 do table.insert(charset, string.char(c)) end for c = 65, 90 do table.insert(charset, string.char(c)) end for c = 97, 122 do table.insert(charset, string.char(c)) end end function M.uuid(length) local res = "" for i = 1, length do res = res .. charset[math.random(1, #charset)] end return res end return M
worker_processes 1; error_log logs/error.log info; events { worker_connections 1024; } http { upstream tomcat{ server localhost:80; } lua_package_path "/usr/local/openresty/nginx/jwt-lua/?.lua;;"; server { listen 8080; set $uid ""; set $uuid ""; location / { access_by_lua " local jwt = require("resty.nginx-jwt") jwt.auth() local u = require("resty.uuid") ngx.var.uuid = u.uuid(64) "; default_type application/json; proxy_set_header uid $uid; proxy_set_header uuid $uuid; proxy_pass http://tomcat; } } }
重啟OpenResty,測試
[root@docker conf]# openresty -s reload [root@docker conf]# curl -i http://localhost:8080/jwtdemo/ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ5YXlhIiwiaWF0IjoxNTMxODk0MDA3LCJpc3MiOiJ5YXlhIiwic3ViIjoieWF5YSIsImV4cCI6MTUzMTkzMDAwN30.vQvpQpIHCmK5QBgIoRR8jhIGeYlHOMYySIr4gHvoZFE" HTTP/1.1 200 Server: openresty/1.13.6.2 Date: Wed, 18 Jul 2018 08:05:45 GMT Content-Type: text/html;charset=UTF-8 Content-Length: 372 Connection: keep-alive uid ==> yaya uuid ==> nhak5eLjQZ73yhAyHLTgZnSBeDa8pa1p3pcpBFvJ4Mv1fkY782UgVr8Islheq03l host ==> tomcat connection ==> close user-agent ==> curl/7.29.0 accept ==> */* authorization ==> Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ5YXlhIiwiaWF0IjoxNTMxODk0MDA3LCJpc3MiOiJ5YXlhIiwic3ViIjoieWF5YSIsImV4cCI6MTUzMTkzMDAwN30.vQvpQpIHCmK5QBgIoRR8jhIGeYlHOMYySIr4gHvoZFE
可以看到,多了一個uuid的請求頭
jwt token生成java示例這里提供一個生成jwt token的java示例
package com.yaya; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * @Description: * @author: jianfeng.zheng * @since: 2018/7/5 下午9:56 * @history: 1.2018/7/5 created by jianfeng.zheng */ public class JWTDemo { public static final String SECRET="5pil6aOO5YaN576O5Lmf5q+U5LiN5LiK5bCP6ZuF55qE56yR"; public static String createJWT(String uid, long ttlMillis) throws Exception { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); Key signingKey = new SecretKeySpec(SECRET.getBytes(), signatureAlgorithm.getJcaName()); Mapheader=new HashMap (); header.put("typ","JWT"); header.put("alg","HS256"); JwtBuilder builder = Jwts.builder().setId(uid) .setIssuedAt(now) .setIssuer(uid) .setSubject(uid) .setHeader(header) .signWith(signatureAlgorithm, signingKey); if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } public static void main(String[]cmd) throws Exception { String s=createJWT("yaya",36000000); System.out.println("Bearer "+s); } }
pom.xml
寫在最后4.0.0 com.yaya jwtdemo 1.0-SNAPSHOT io.jsonwebtoken jjwt 0.6.0
這里只是解決了文章開頭提到的第一個問題,接口需要知道是誰調(diào)用了接口,第二個問題,用戶能不能調(diào)接口目前考慮用aop在應(yīng)用層實現(xiàn),后續(xù)也會繼續(xù)更新.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/40008.html
摘要:現(xiàn)在用方式調(diào)用接口,中使用方式輸入內(nèi)容日志平臺網(wǎng)關(guān)層基于。日志平臺網(wǎng)關(guān)層基于到此為止,提取經(jīng)過網(wǎng)關(guān)的接口信息,并將其寫入日志文件就完成了,所有的接口日志都寫入了文件中。 背景介紹 1、問題現(xiàn)狀與嘗試 沒有做日志記錄的線上系統(tǒng),絕對是給系統(tǒng)運(yùn)維人員留下的坑。尤其是前后端分離的項目,后端的接口日志可以解決對接、測試和運(yùn)維時的很多問題。之前項目上發(fā)布的接口都是通過Oracle Service...
摘要:下使用快速搭建灰度網(wǎng)關(guān)簡介是新浪開源的一個可以動態(tài)設(shè)置分流策略的灰度發(fā)布系統(tǒng),工作在層,基于和開發(fā),使用作為分流策略數(shù)據(jù)庫,可以實現(xiàn)動態(tài)調(diào)度功能。目前在京東如實時價格秒殺動態(tài)服務(wù)單品頁列表頁等都在使用架構(gòu),其他公司如淘寶去哪兒網(wǎng)等。 Mac下使用ABTestingGateway快速搭建灰度網(wǎng)關(guān) ABTestingGateway簡介 ABTestingGateway 是新浪開源的一個可以...
摘要:我們整個監(jiān)控的部分,沒有采用社區(qū)流行的,而是自己實現(xiàn)了一套。但是對于前端來說,只暴露一個入口,引入一個反代即可。簡介是一個為了讓部署微服務(wù)更加便捷而誕生的現(xiàn)代反向代理負(fù)載均衡工具。配置熱更新,支持多種后端。將請求轉(zhuǎn)發(fā)到統(tǒng)一認(rèn)證服務(wù)。 前言 對于監(jiān)控這塊,我們基于prometheus實現(xiàn),當(dāng)然做了大量的優(yōu)化,包括前面所講到的配置接口化。我們整個監(jiān)控的UI部分,沒有采用社區(qū)流行的grafa...
摘要:我們整個監(jiān)控的部分,沒有采用社區(qū)流行的,而是自己實現(xiàn)了一套。但是對于前端來說,只暴露一個入口,引入一個反代即可。簡介是一個為了讓部署微服務(wù)更加便捷而誕生的現(xiàn)代反向代理負(fù)載均衡工具。配置熱更新,支持多種后端。將請求轉(zhuǎn)發(fā)到統(tǒng)一認(rèn)證服務(wù)。 前言 對于監(jiān)控這塊,我們基于prometheus實現(xiàn),當(dāng)然做了大量的優(yōu)化,包括前面所講到的配置接口化。我們整個監(jiān)控的UI部分,沒有采用社區(qū)流行的grafa...
閱讀 738·2021-10-09 09:44
閱讀 2005·2021-09-22 15:54
閱讀 5043·2021-09-22 10:55
閱讀 1435·2019-08-29 18:41
閱讀 771·2019-08-29 11:24
閱讀 2099·2019-08-28 18:20
閱讀 1025·2019-08-26 11:51
閱讀 3044·2019-08-26 11:00