1、建立安全的微服務html
在微服務架構中實現可靠且強大的安全實現很是重要。微服務的體系結構嚮應用程序公開了多個入口點,而且通訊可能須要多個網絡躍點,所以未受權訪問的風險很高。這須要比傳統應用程序更多的計劃。此外,因爲REST服務的如下功能,使用REST端點的微服務中的安全性很難實現:前端
REST基於無狀態協議(HTTP):必須爲每一個請求傳輸在客戶端和微服務之間傳輸的任何敏感信息。java
REST基於基於文本的協議(HTTP):每一個請求發送的信息均可供任何竊聽通訊的人使用,由於HTTP是純文本協議。任何敏感數據都是可見的,可能會被第三方捕獲。web
REST沒有定義傳輸敏感數據的獨特標準方法:至少有三種方法能夠在REST中以安全的方式傳輸信息,包括OAuth2,OpenID Connect(OIDC)和JSON Web令牌(JWT)。算法
爲避免互操做性問題和所提到的複雜性,請使用MicroProfile JWT規範來保護在您的微服務之間傳遞的信息。編程
該規範使用JSON Web令牌(JWT),這是一種基於令牌的身份驗證,它定義了一種算法,以保證在基於REST的應用程序中以可靠和安全的方式傳輸任何敏感信息。api
基於令牌的身份驗證工做流涉及如下實體:瀏覽器
Issuer安全
在聲明身份後發出安全令牌。 這一般是一個獨特的微服務,做爲身份提供者,提供JWT令牌生成器。服務器
Client
從發行者請求令牌的微服務。
Subject
令牌中的信息所指的我的、系統或實體。
Resource Server
消耗令牌的微服務。
資源服務器使用如下令牌工做流:
1 從名爲Authorization的字段中的標頭中提取安全性令牌。
2 驗證令牌檢查簽名,加密和到期檢查。
3 提取有關主題的信息。
4 爲主題建立安全上下文。
2、JWT內容完整性
爲了不任何數據操做並確保從發送方到最終目的地的消息的完整性,JWT規範要求JWT數據必須通過簽名或加密。
簽名:使用私鑰來保證內容來自可靠的來源。 簽名應符合JSON Web簽名(JWS)規範。
加密:使用私鑰加密JSON Web加密(JWE)規範以後的內容。
JWT結構
生成的JWT內容使用如下格式進行組織:
xxxxxxxx.yyyyyyyyy.zzzzzzzzz
全部塊都使用base64編碼進行編碼,這樣幾乎不可能被破解。
First Block xxxxxxxx
表示包含用於處理第二個塊的信息的JWT標頭,例如散列算法和令牌類型,即JWT。
Second Block yyyyyyyyy
表示包含添加到JWT的全部聲明的JWT有效內容。 若是郵件已加密,則會對內容進行加密,而後使用base64編碼進行編碼。
Third Block zzzzzzzzz
表示標頭和有效負載的簽名,保證在傳輸過程當中沒有任何更改。
在下面的示例中,您有一個JWT,三個塊中的每個都用點分隔。
1. JWT頭,包含散列算法和base64中編碼的令牌類型。
2來自JWT的有效載荷,採用base64編碼格式
3標頭和有效載荷的簽名在base64中編碼。
3、在REST端點中傳輸JWT
須要發送敏感信息的REST端點必須首先向JWT令牌提供程序請求令牌。 在下圖中,Microservice A使用JWT微服務提供程序進行身份驗證。 驗證身份驗證後,JWT微服務提供程序返回一個JWT字符串,微服務A可使用該字符串進行微服務B的身份驗證.Microsvice Service A使用Authorization HTTP頭字段發送JWT值。 爲了被微服務B接受,Authorization頭字段必須包含Bearer前綴,後跟JWT字符串。
4、用Java建立JWT
爲了與每一個服務提供單一功能的微服務架構保持一致,您能夠建立一個微服務,爲須要利用令牌的全部其餘微服務提供JWT。 這種微服務被稱爲JWT提供商。 Java提供了諸如Auth0、Jose4J和Nimbus JOSE JWT之類的庫來建立JWT。 本文使用Nimbus JOSE JWT實現。
什麼是Nimbus JOSE JWT?
Nimbus JOSE + JWT是一個開源(Apache 2.0)Java庫,它實現了Javascript對象簽名和加密( Javascript Object Signing and Encryption:JOSE)規範套件以及密切相關的JSON Web Token(JWT)規範。
該庫能夠建立、檢查、序列化和解析如下JOSE和JWT對象:
Plain (unsecured) JOSE objects.
JSON Web Signature (JWS) objects.
JSON Web Encryption (JWE) objects.
JSON Web Key (JWK) objects and JSON Web Key (JWK) Sets.
Plain, signed and encrypted JSON Web Tokens (JWTs).
因此,在本文中,Nimbus JOSE JWT是做爲一個JWT生成器而存在!
在部署了JWT生成器(Nimbus JOSE JWT)以後,生成的字符串將用於訪問安全的微服務。
如下示例使用此庫建立JWT的解釋:
1.Create claims as a JSON object, and defines the registered and default claims using the Claims enum values.
建立聲明做爲JSON對象,並使用聲明枚舉值定義已聲明和默認聲明。
2 Instantiate the object that signs the payload. You must provide a private key, created with the ssh-keygen command, to instantiate a JWSSigner object to sign the claims.
實例化簽署有效負載的對象。 您必須提供使用ssh-keygen命令建立的私鑰,以實例化JWSSigner對象以對聲明進行簽名。
3 Parse the claims into a JWTClaimsSet object.
將聲明解析爲JWTClaimsSet對象。
4 Instantiate the JWSHeader object with the appropriate algorithm.
使用適當的算法實例化JWSHeader對象。
5 Sign the claims and the header:
簽署聲明和標題:
6 Create a base64-encoded content that follows the JWT structure.
建立遵循JWT結構的base64編碼內容。
7 Create the String that represents the JWT structure.
建立表示JWT結構的String。
接下來,咱們經過一個小的實驗,展示如何建立JWT。
第一個源碼文件:AuthzResource.java
首先看一個java源碼--AuthzResource.java, 該方法負責公開提供微模塊使用的JWT令牌字符串的REST端點。說白了,就是申請生成token的入口(本實驗中,能夠從瀏覽器可以訪問,輸入用戶名和密碼,而後返回生成的token)
1從Credentials對象中讀取用戶名
2使用電子郵件中@字符前面的字符串做爲用戶名。
3從Credentials對象中讀取密碼。
4將密碼定義爲用戶名和-secret字符串的串聯。
5填充JWT的標準聲明,例如preferred_username和upn。
6填充名爲cpny的自定義聲明,表示負責生成令牌的公司名稱。
7定義必須用做基線的JSON模板文件名。
第二個源碼文件:TokenUtils.java
令牌建立過程,沒有寫在AuthzResource.java中,而是由TokenUtils類的generateTokenString方法實現,這樣作是爲了鬆耦合(這樣之後前端的應用能夠方便調用令牌建立的方法,也是爲了最小化JSON建立編碼)。
檢查TokenUtils類中的generateTokenString方法,是如何建立或、更新現有JWT令牌的。
1更改解析器,以便支持JSON格式。
2 將文件轉換爲JSON Object實例。
3 Capture當前時間戳。
4使用timeToLive變量更新到期時間。
5 若是先前已建立聲明,請重複使用現有的到期時間。
6 使用訪問現有Claim對象時的最後一個時間戳更新。
7 使用現有Claim對象對用戶進行身份驗證時,使用時間戳更新。
8 使用Claim對象中的過時時間戳更新。
9 將REST端點獲取的全部字段(例如用戶名和密碼)複製到Claim對象。
檢查generateTokenString方法如何簽署JSON Web令牌。
1讀取用於簽署JWT令牌的私鑰。
2建立RSA簽名者。
3解析上一步中填充的聲明。
4使用RSA算法按照標準,使用私鑰進行簽名,並遵循JWT標準。
5使用標題和JWT聲明來實現JWT。
5建立JWT而不簽名。
6生成要用做JWT的文本。
4、實驗展示:部署JSON Web令牌生成器
在本小節實驗中,咱們主要修改兩個源碼文件:AuthzResource.java和TokenUtils.java.
其中,AuthzResource.java是負責接收客戶端發過來的用戶名密碼,並將接收到的用戶名密碼傳給TokenUtils.java,生成token,而後將生成的token返回給前臺。
TokenUtils.java是用根據AuthzResource.java傳過來的用戶名和密碼,生成token的。
咱們看實驗內容:
首先經過JBDS導入一個已有的maven項目:
經過展開左窗格JBoss Developer Studio中Project Explorer選項卡中的microservice-authz項打開AuthzResource類,而後單擊microservice-authz→Java Resources→src / main / java→io.microprofile.showcase.tokens將其展開。 雙擊AuthzResource.java文件。
AuthzResource.java是負責檢查從REST端點,捕獲請求中的用戶名和密碼。
AuthzResource.java中的createTokenForCredentials方法,是用於獲取web輸入的用戶名和密碼:
AuthzResource.java中的下面代碼:
用到了 HashMap對象。
HashMap是一種支持快速存取的數據結構、是使用很是多的Collection,它是基於哈希表的 Map 接口的實現,以key-value的形式存在。HashMap中,key-value老是會當作一個總體來處理,系統會根據hash算法來來計算key-value的存儲位置,咱們老是能夠經過key快速地存、取value。
UPN,全程是:User Principal Name,也就是用戶名。
preferred_username:Shorthand name by which the End-User wishes to be,也便是用戶名的簡寫。(https://www.iana.org/assignments/jwt/jwt.xhtml)
也就是說,這段將upn和preferred_username默認聲明,添加到HashMap實例的端點,而後將與他們傳給遞給TokenUtils實用程序類,用於TokenUtils用於構建令牌。
經過展開JBoss Developer Studio左側窗格中Project Explorer選項卡中的microservice-authz項打開TokenUtils類,而後單擊microservice-authz→Java Resources→src / main / java→io.microprofile.showcase.tokens將其展開。 雙擊TokenUtils.java文件。
在TokenUtils.java的generateTokenString方法中,並將名爲dvlpr_nm的新的聲明添加到jwtContent對象,使用davidwei做爲來源:
編譯運行:
微服務運行成功之後,使用REST Client經過http驗證AuthzResource的端點,併發送用戶名和密碼:
在Headers選項卡中驗證狀態代碼是否爲200 OK。
獲得token:
複製id_token內容供後面使用:從RESTClient Firefox插件中選擇id_token中的內容,而後按Ctrl + C將令牌複製到剪貼板。
此時,查看微服務的console log:
第三個源碼:Translate.java驗證token的有效性
經過展開JBoss Developer Studio左側窗格中Project Explorer選項卡中的microservice-authz項來執行Translate類,而後單擊microservice-authz→Java Resources→src / main / java→io.microprofile.showcase.authz將其展開。 右鍵單擊Translate.java文件,選擇Run As→Java Application。
在Console選項卡中,將顯示Enter token:提示符。 在提示後單擊鼠標,而後按Ctrl + V粘貼id_token。 按Enter鍵處理JWT內容。
從輸出中能夠看到token被解析的用戶名,以及claim name:davidwei
5、使用JSON Web令牌(JWT)進行身份驗證
JSON Web Token(JWT)是訪問MicroProfile JWT安全應用程序的網關。要使用受JWT保護的應用程序進行身份驗證,HTTP請求必須使用Authorization標頭字段發送HTTP標頭。該值必須之前綴Bearer(開頭,而且必須包含憑證提供程序微服務生成的JWT字符串。
配置MicroProfile JWT Fraction
MicroProfile JWT Fraction提供了生成JWT令牌所需的依賴關係,如Auth0、Jose4J和Nimbus JOSE JWT。除了提供依賴關係以外,在JWT提供程序中包含此 Fraction還可簡化配置並容許自定義JWT的各個方面的配置,例如:
Expiration Grace Period
JWT到期的時間量(以秒爲單位)
Issuer information
JWT令牌頒發者的URI
Public Key Information
JWT令牌簽名者的公鑰
要配置這些參數,必須首先在微服務提供程序的pom.xml文件中包含MicroProfile JWT部分,並更新project-defaults.yml文件以包含如下內容:
1 Customizes the issuer for each request
2 Customizes the amount of time for the endpoint grace period
3 Customizes the public key used by the endpoint
6、爲JWT登陸模塊配置WildFly Swarm
任何使用JWT進行身份驗證,並開發用於WildFly Swarm的微服務都須要src/main/resources目錄中project-defaults.yml文件中的如下內容:
1 配置WildFly Swarm以使用JWTLoginModule類進行身份驗證
2 使用能夠對應用程序進行身份驗證的角色及其各自的用戶來查找屬性文件
3 自定義Undertow以支持JWT
4 配置Undertow模塊以使用標頭接受身份驗證
JWTLoginModule類負責將JWT中定義的組,轉換爲Java身份驗證和受權服務(JAAS)角色。
7、使用JAAS捕獲用戶信息
WildFly Swarm登陸模塊,未來自JWT的全部信息轉換爲符合JAAS標準的註釋和類。 這使咱們可使用JAAS註釋(如@ javax.annotation.security.DeclareRoles)聲明容許哪些JWT角色訪問應用程序。
public class MyRoles { ...@DeclareRoles({"VIP", "Voter", "Alumni"})
要容許特定角色調用方法,請使用@RolesAllowed註釋:
@RolesAllowed("Alumni") public String fooBar() { ...
8、使用JAAS以編程方式保護微服務
在須要低級安全管理的微服務(例如基於時間的限制)中,使用JAAS建立特定規則。 例如,能夠在天天的特定時段內啓用對用戶的訪問權限。
將javax.ws.rs.core.SecurityContext對象注入類中以獲取用戶信息,例如用戶名。 在如下示例中,用戶在下午1:00以後被授予訪問權限。
@Inject private SecurityContext context; public String fooBar() { ... String name=context.getUserPrincipal().getName(); if("customer".equals(name) && new Date().getHours()>13){ ... } ...
要捕獲特定於JWT的信息(例如聲明),必須將UserPrincipal對象強制轉換爲org.eclipse.microprofile.jwt.JsonWebToken對象,以下所示:
JsonWebToken token = (JsonWebToken) securityContext.getUserPrincipal();
這種方法經常使用於,在以編程方式提取聲明數據(例如令牌到期或某些自定義屬性)的狀況。
9、實驗展現:保護微服務端點
在實驗中,配置hola微服務以使用JWT對用戶進行身份驗證。
在JBoss Developer Studio中,經過展開JBoss Developer Studio左窗格中Project Explorer選項卡中的hola項來打開project-defaults.yml文件。
單擊hola→Java Resources→src / main / resources以展開它。 雙擊project-defaults.yml文件。
更新文件以支持使用JWT進行身份驗證。
在login-modules部分中,使用
org.wildfly.swarm.microprofile.jwtauth.deployment.auth.jaas更新代碼屬性.JWTLoginModule。
配置微服務REST端點以容許具備Alumni角色的用戶訪問。
從HolaResource類更新secureHola方法,以僅容許訪問屬於Alumni角色的用戶。
在JBoss Developer Studio中,經過展開左窗格中Project Explorer選項卡中的hola項來打開HolaResource類。
單擊hola→Java Resources→src / main / java→com.redhat.training.msa.hola.rest以展開它。 雙擊HolaResource.java文件。
將@RolesAllowed方法級註釋添加到secureHola方法,以僅容許訪問屬於Alumni角色的用戶。
從請求中檢索JWT並使用此信息建立對客戶端的響應。
將SecurityContext實例注入HolaResource實例以獲取用戶的身份驗證信息。
在HolaResource類中,添加SecurityContext屬性和@Context註釋。
實現secureHola方法,分別從username和expirationTime變量中的securityContext屬性捕獲用戶名和到期時間戳。
在HolaResource類中,添加如下代碼:
啓動hola微服務。
在新的終端窗口中,運行如下命令:
接下來,咱們驗證hola微服務僅接受使用JWT進行身份驗證的用戶。
在工做站VM上啓動Firefox,而後單擊瀏覽器工具欄中的RESTClient插件。
選擇GET做爲方法。 在URL表單中,輸入http:// localhost:8080 / api / hola-secure,而後單擊「發送」。
在Headers選項卡中驗證狀態代碼是401 Unauthorized。
啓動authz微服務。 微服務生成可用於微文檔會議應用程序中的任何應用程序的JWT認證令牌。
在新的終端窗口中,運行如下命令:
驗證hola微服務是否接受authz微服務生成的JWT。
在Firefox中打開一個新選項卡,而後單擊瀏覽器工具欄中的RESTClient插件。
單擊發送。在「標題」選項卡中,驗證「狀態代碼」是否爲200。
在「響應」選項卡中,驗證響應是否與如下內容匹配:
複製id_token屬性內容以用做身份驗證過程的一部分。
在用於調用hola-secure端點的Firefox選項卡中,選擇Headers→Custom Header。 使用如下值填寫表單,使用以前捕獲的id_token更新Bearer字符串以後的文本。
單擊發送。
在「標題」選項卡中,驗證「狀態代碼」是否爲200。
成功訪問: