Pac4j文件翻譯(3.0)
翻譯有很多不準確的地方,只作為自己學習的筆記來用。歡迎批評指正。
Pac4j簡介
pac4j是一個簡單而強大的安全引擎,用於Java對使用者進行身份驗證、獲取其配置檔案和管理授權,以確保web應用程式安全。它提供了一套完整的概念和元件。它基於Java 8,並在Apache 2許可下使用。它可用於大多數框架/工具和支援大多數認證/授權機制。
1. 主要概念和元件介紹
1) client(客戶端): 客戶端表示身份驗證機制(流)。它執行登入過程並返回一個使用者概要檔案。間接客戶端用於UI身份驗證,而直接客戶端則用於web服務身份驗證。
2) authenticator(認證器):HTTP客戶端驗證憑證需要一個認證器。它是ProfileService的一個子元件,該元件驗證憑證,同時也處理使用者的建立、更新和移除操作。
3) authorizer(授權器):授權器用於檢查已認證使用者的授權或當前web上下文中的授權。
4) matcher(匹配器):一個匹配器定義了security是否必須應用到安全過濾器。
5) config(配置):配置通過客戶端、授權器和匹配器定義安全配置。
6) user profile(使用者概要檔案):使用者概要是已認證使用者的概要。它有一個識別符號、屬性、角色、許可權、一個“記住-我”屬性和一個連結識別符號。
7) web context(web上下文):web上下文是對pac4j實現特有的HTTP請求和響應的抽象,而關聯的SessionStore表示會話的實現。
8) security filter(安全過濾器):(或者不論使用什麼原理去攔截的HTTP請求)根據客戶端和授權配置,通過檢查使用者已認證和已授權來保護一個URL。如果使用者沒有被認證,它對直接客戶端展示認證,對間接客戶端啟動登入程序。
9) callback controller(回撥控制器):對間接客戶端結束登入程序後的回撥。
10) logout controller(退出登入控制器):處理應用程式和/或身份伺服器登出。
2 認證機制
client支援多種認證機制:OAuth、SAML、CAS、OpenID Connect、HTTP、OpenID、Google APP Engine、Kerberos(SPNEGO)
Authenticators:LDAP、SQL、JWT、MongoDB、CouchDB、IP address、REST API
通常,授權器被定義在應用的安全配置檔案中。有不同的授權器變數如下:
Roles/permissions - Anonymous/remember-me/(fully) authenticated - Profile type, attribute
CORS - CSRF - Security headers - IP address, HTTP method
大多數pac4j元件實現DefaultAuthorizationChecker元件來使用pac4j的邏輯和授權。因此,下列授權器可以通過短名稱自動找到:
- hsts:StrictTransportSecurityHeader 認證器
- nosniff: XContentTypeOptionsHeader 認證器
- noframe: XFrameOptionsHeader 認證器
- xssprotection: XSSProtectionHeader 認證器
- nocache: CacheControlHeader 認證器
- securityheaders: as a shortcut for hsts,nosniff,noframe,xssprotection,nocache
- csrfToken: CsrfTokenGeneratorAuthorizer 認證器
- csrfCheck: CsrfAuthorizer 認證器
- csrf as a shortcut for csrfToken,csrfCheck
- isAnonymous:IsAnonymousAuthorizer 認證器
- isAuthenticated:IsAuthenticatedAuthorizer 認證器
- isFullyAuthenticated:IsFullyAuthenticatedAuthorizer 認證器
- isRemembered: IsRememberedAuthorizer 認證器
- allowAjaxRequests for a default configuration of the CorsAuthorizer authorizer with the Access-Control-Allow-Origin header set to *.
這些短名稱都作為常量定義在DefaultAuthorizers類中。
4 Matchers 匹配器
1) 定義(definition)
pac4j提供了一個安全模型和引擎(特定的行為)。安全過濾器負責url保護、請求認證和可選授權。
在某些情況下,你可能想要繞開安全過濾器(security filter)。而使用匹配器引數(matchers parameter)可以做到,引數通常是一個匹配名稱的列表。一個匹配器通常定義在安全配置檔案中(security configuration)。
2) 實現(implementation)
一個匹配器可以通過實現Matcher介面來定義。它只有一個方法:
boolean matches(WebContext context);
這個方法表明安全過濾器(security filter)是否必須被應用。
一些預設的matcher匹配器可以用。(但是你可以開發自己的匹配器):
- PathMatcher:允許你從安全檢查中排除一些路徑。
- HeaderMatcher:允許你檢查一個給出的頭部是否是空或匹配一個特定的表示式。
- HttpMethodMatcher:允許你檢查HTTP請求的方法是否是你預期的已定義的方法之一。
5 Security configuration 安全配置
1) 配置元件
大多數pac4j實現中,安全配置可以通過配置物件去定義。
它有這些必須的:
- PasswordEncoders:密碼加密器
- Authenticators:認證器
- Clients:客戶端
- Authorizers:授權器
- Matchers:匹配器
例如:
Config 1
FacebookClient facebookClient = new FacebookClient("145278422258960", "be21409ba8f39b5dae2a7de525484da8");
TwitterClient twitterClient = new TwitterClient("CoxUiYwQOSFDReZYdjigBA", "2kAzunH5Btc4gRSaMr7D7MkyoJ5u1VzbOOzE8rBofs");
ParameterClient parameterClient = new ParameterClient("token", new JwtAuthenticator(salt));
Config config = new Config("http://localhost:8080/callback", facebookClient, twitterClient, parameterClient);
config.addAuthorizer("admin", new RequireAnyRoleAuthorizer<>("ROLE_ADMIN"));
config.addAuthorizer("custom", new CustomAuthorizer());
config.addMatcher("excludedPath", new ExcludedPathMatcher("^/facebook/notprotected\\.jsp$"));
您還可以使用一箇中間客戶端物件來構建Config 1。例如Config 2
Config 2
Clients clients = new Clients("http://localhost:8080/callback", facebookClient, twitterClient, parameterClient);
Config config = new Config(clients);
在上面的例子中,你可以為所有的客戶端定義這些內容:
- 一個回撥地址(callback URL)和一個回撥地址解析器(CallbackUrlResolver):
clients.setCallbackUrl(callbackUrl);
clients.setCallbackUrlResolver(callbackUrlResolver);
- 一個非同步請求解析器(AjaxRequestResolver):
clients.setAjaxRequestResolver(ajaxRequestResolver);
- 一個認證生成器(AuthorizationGenerator):
clients.addAuthorizationGenerator(authorizationGenerator);
2) pac4j-config module(pac4j配置模組)
pac4j-config模組收集所有pac4j工具來定義這個配置物件。當前,只有一個元件允許你從一系列屬性中建立客戶端:PropertiesConfigFactory. Dropwizard, CAS 和 Knox用到該類。
注意:當需要的時候,這些依賴必須被明確宣告。(如果你想要使用SAML、OAuth…)
例如:
pac4j:
callbackUrl: /callback
clientsProperties:
facebook.id: 145278422258960
facebook.secret: be21409ba8f39b5dae2a7de525484da8
saml.keystorePath: resource:samlKeystore.jks
saml.keystorePassword: pac4j-demo-passwd
saml.privateKeyPassword: pac4j-demo-passwd
saml.identityProviderMetadataPath: resource:metadata-okta.xml
saml.maximumAuthenticationLifetime: 3600
saml.serviceProviderEntityId: http://localhost:8080/callback?client_name=SAML2Client
saml.serviceProviderMetadataPath: sp-metadata.xml
anonymous: fakevalue
ldap.type: direct
ldap.url: ldap://ldap.jumpcloud.com:389
ldap.useStartTls: false
ldap.useSsl: false
ldap.dnFormat: uid=%s,ou=Users,o=58e69adc0914b437324e7632,dc=jumpcloud,dc=com
ldap.usersDn: ou=Users,o=58e69adc0914b437324e7632,dc=jumpcloud,dc=com
ldap.principalAttributeId: uid
ldap.principalAttributes: firstName,lastName
ldap.enhanceWithEntryResolver: false
formClient.loginUrl: /login.html
formClient.authenticator: ldap
下面是一項你可以來定義客戶端(,密碼加密器和認證器)的一些屬性:
可用屬性 | 用法 |
---|---|
encoder.spring.type (bcrypt, noop, pbkdf2, scrypt or standard), encoder.spring.bcrypt.length, encoder.spring.pbkdf2.secret, encoder.spring.pbkdf2.iterations, encoder.spring.pbkdf2.hashWidth, encoder.spring.scrypt.cpuCost, encoder.spring.scrypt.memoryCost, encoder.spring.scrypt.parallelization, encoder.spring.scrypt.keyLength, encoder.spring.scrypt.saltLength and encoder.spring.standard.secret | 根據所提供的屬性和命名的編碼器定義一個SpringPasswordEncoder。 |
encoder.shiro (if no specific properties are required), encoder.shiro.generatePublicSalt, encoder.shiro.hashAlgorithmName, encoder.shiro.hashIterations and encoder.shiro.privateSalt | 根據所提供的屬性和命名的編碼器定義一個ShiroPasswordEncoder。 |
ldap.type, ldap.dnFormat, ldap.principalAttributes,ldap.principalAttributeId, ldap.principalAttributePassword, ldap.subtreeSearch, ldap.usersDn, ldap.userFilter, ldap.enhanceWithEntryResolver, ldap.trustCertificates, ldap.keystore, ldap.keystorePassword, ldap.keystoreType, ldap.minPoolSize, ldap.maxPoolSize, ldap.poolPassivator, ldap.validateOnCheckout, ldap.validatePeriodically, ldap.validatePeriod, ldap.failFast, ldap.idleTime, ldap.prunePeriod, ldap.blockWaitTime, ldap.url, ldap.useSsl, ldap.useStartTls, ldap.connectTimeout, ldap.providerClass, ldap.allowMultipleDns, ldap.bindDn, ldap.bindCredential, ldap.saslRealm, ldap.saslMechanism, ldap.saslAuthorizationId, ldap.saslSecurityStrength and ldap.saslQualityOfProtection | 根據所提供的屬性和命名ldap或ldap來定義LdapAuthenticator。 |
db.dataSourceClassName, db.jdbcUrl, db.userAttributes, db.userIdAttribute, db.usernameAttribute, db.userPasswordAttribute, db.usersTable, db.username, db.password, db.autoCommit, db.connectionTimeout, db.idleTimeout, db.maxLifetime, db.connectionTestQuery, db.minimumIdle, db.maximumPoolSize, db.poolName, db.initializationFailTimeout, db.isolateInternalQueries, db.allowPoolSuspension, db.readOnly, db.registerMbeans, db.catalog, db.connectionInitSql, db.driverClassName, db.transactionIsolation, db.validationTimeout, db.leakDetectionThreshold, db.customParamKey, db.customParamValue, db.loginTimeout, db.dataSourceJndi and db.passwordEncoder | 要基於所提供的屬性定義DbAuthenticator,並命名為db或db.N |
rest.url | 按照所提供的屬性和命名rest或rest.n來定義RestAuthenticator |
anonymous | 定義AnonymousClient,該值將被忽略 |
directBasicAuth.authenticator | 根據所提供的屬性定義DirectBasicAuthClient |
saml.keystorePassword, saml.privateKeyPassword, saml.keystorePath, saml.identityProviderMetadataPath, saml.maximumAuthenticationLifetime, saml.serviceProviderEntityId, saml.serviceProviderMetadataPath, saml.destinationBindingType, saml.keystoreAlias | 根據所提供的屬性定義SAML2Client |
cas.loginUrl, cas.protocol | 根據所提供的屬性來定義CasClient |
oidc.type (google or azure), oidc.id, oidc.secret, oidc.scope, oidc.discoveryUri, oidc.useNonce, oidc.preferredJwsAlgorithm, oidc.maxClockSkew, oidc.clientAuthenticationMethod, oidc.customParamKey1, oidc.customParamValue1, oidc.customParamKey2,oidc.customParamValue2 | 根據所提供的屬性定義OpenID connect client |
formClient.authenticator, formClient.loginUrl, formClient.usernameParameter formClient.passwordParameter | 根據所提供的屬性定義FormClient |
indirectBasicAuth.authenticator, indirectBasicAuth.realName | 根據所提供的屬性定義IndirectBasicAuthClient |
facebook.id, facebook.secret, facebook.scope, facebook.fields | 基於所提供的屬性定義一個FacebookClient |
twitter.id, twitter.secret | 根據所提供的屬性來定義TwitterClient |
github.id, github.secret | 根據所提供的屬性來定義GitHubClient |
dropbox.id, dropbox.secret | 根據所提供的屬性來定義DropBoxClient |
windowslive.id, windowslive.secret | 根據所提供的屬性定義windowslivecent |
yahoo.id, yahoo.secret | 根據所提供的屬性來定義一個YahooClient |
linkedin.id, linkedin.secret, linkedin.fields, linkedin.scope | 根據所提供的屬性來定義一個LinkedIn2Client |
foursquare.id, foursquare.secret | 根據所提供的屬性來定義一個FoursquareClient |
google.id, google.secret, google.scope | 根據所提供的屬性定義一個Google2Client |
注意:
- 你可以在屬性末尾新增一個數字來定義多個相同型別的client:cas.loginUrl.2, oidc.type.5
- passwordEncoder屬性必須設定為已定義的passwordEncoder的名稱,如:encoder.spring or encoder.shiro.3
- Authenticator屬性必須設定為已定義的 身份驗證器( Authenticator)的名稱,如: ldap or db.1或者隱含的值:testUsernamePassword or testToken.
6 User profile 使用者概要
當用戶成功通過pac4j進行身份驗證時,他的資料將從身份提供程式中檢索,並構建一個使用者概要。他的概要有:
- an identifier (getId()):識別符號
- attributes (getAttributes(), getAttribute(name)):屬性
- authentication-related attributes (getAuthenticationAttributes(), getAuthenticationAttribute(name)):認證相關屬性
- roles (getRoles()):角色
- permissions (getPermissions()):許可權
- a client name (getClientName()):客戶端名稱
- a remember-me nature (isRemembered()):記住我的性質
- a linked identifier (getLinkedId()):連結識別符號
1) Identifier
每個使用者配置檔案必須具有唯一的識別符號。因此,當構建使用者概要檔案時,pac4j客戶端使用的是概要識別符號——從身份提供者那裡執行惟一性的值。
這可以很好地處理來自同一身份提供者的概要檔案,儘管在使用多個身份提供者時這可能會成為一個問題。我們可能在標識提供者中選擇的識別符號之間發生衝突。為了避免這個問題,在檔案識別符號之前新增概要檔案類名,要有一個”型別識別符號”。
例如:
profile.getId() // 00001
profile.getTypedId() // org.pac4j.oauth.profile.facebook.FacebookProfile#00001
2) 屬性
使用者概要檔案具有屬性,這些屬性來自於從身份提供者檢索的資料(在轉換之後)。
3) 認證相關屬性
一些身份提供者將包括與身份驗證本身相關的屬性,比如身份驗證方法、身份驗證有效的時間期限,或關於身份提供者的元資料。這些屬性與使用者的屬性是分開儲存的。
4) 角色和許可權
角色和許可權可以通過addRole(role), addRoles(roles), addPermission(permission) and addPermissions(permissions) 方法新增到使用者概要檔案中。
它們通常是在一個授權生成器(AuthorizationGenerator)中計算的。
5) 客戶端名稱
在登入過程中,客戶端的名稱通過setClientName(name)儲存到使用者概要中,隨後可以通過getClientName()方法檢索到。
6) 記住我
使用者概要可以通過setRemembered(boolean)方法定義為記住我,而不是完全通過認證。isRemember()方法人會使用者概要是否被記住。
7) CommonProfile的通用方法
Method | Type | Returns |
---|---|---|
getEmail() | String | Email 屬性 |
getFirstName() | String | first_name 屬性 |
getFamilyName() | String | family_name 屬性 |
getDisplayName() | String | display_name 屬性 |
getUsername() | String | username 屬性 |
getGender() | Gender | gender 屬性 |
getLocale() | Locale | locale 屬性 |
getPictureUrl() | URI | picture_url 屬性 |
getProfileUrl() | URI | profile_url 屬性 |
getLocation() | String | location 屬性 |
asPrincipal() | Principal | 一個包含當前已認證使用者名稱的物件 |
isExpired() | boolean | 如果當前配置檔案過期,則為false |
8) 配置檔案定義(Profile definition)
配置檔案類和屬性通過實現 ProfileDefinition類來定義。
setProfileFactory方法允許您定義例項類,以便返回給使用者配置檔案,而primary 和 secondary方法允許您用特定的轉換器定義屬性。
許多屬性轉換器以及存在: BooleanConverter, ColorConverter… 檢視包 org.pac4j.core.profile.converter
因此,newProfile方法返回一個新的類例項,而convertAndAdd方法如果有相關的轉換器會轉換屬性,並將它們新增到概要檔案中。
9) 配置層級(Profile hierarchy)
事實上,大多數Client都不會返回一個CommonProfile,而是一個特定的配置,如:FacebookProfile, the OidcProfile…:
- (部分地)用特定的實現覆蓋公共概要的公共方法
- 為他們的特定屬性新增特定的getter。
10) 連結識別符號
每個使用者配置檔案可能有一個連結識別符號,它是另一個使用者配置檔案的識別符號。這種方式,使得兩個使用者配置檔案都被聯絡起來,它允許你通過一個帳戶進行身份驗證,這個使用者可以載入定義在第一個使用者中的連結使用者,特別是通過使用LoadLinkedUserAuthorizationGenerator生成的。
7 會話儲存和儲存(SessionStore and Store)
- Session Store
WebContext對於處理HTTP請求和響應是一個抽象的類。為了處理session,它依賴於一個SessionStore,可以通過getSessionStore方法獲得。
SessionStore有如下方法:
- getOrCreateSessionId: 獲取或建立會話識別符號並在必要時用它初始化會話
- get: 從會話中獲取屬性
- set: 給會話設定屬性
- destroySession: 銷燬潛在的web會話
- getTrackableSession: 把本機會話作為可跟蹤物件(用於反向通道登出)
- buildFromTrackableSession: 從可跟蹤的會話(用於反向通道登出)構建一個新的會話
- renewSession: 通過將所有資料複製到一個新的資料來更新本機會話
例如,J2EContext當前使用J2ESessionStore,依賴於J2ESession。在遊戲中,我們有一個特定的基於快取的PlayCacheSessionStore,以及在Knox中有一個基於cookie的KnoxSessionStore。
ProfileStorageDecision定義與該概要檔案相關的決策,通過該決策,我們判斷是否必須將其讀取並儲存到web會話中。它被DefaultSecurityLogic使用:
- DefaultProfileStorageDecision在預設情況下被設定,這對於一個只使用間接客戶端或直接客戶端的web應用程式是適合的。
- Store
在某些情況下,需要一個快取機制。在pac4j中,通過Store這個概念定義。
它有如下幾個方法:
- get:從store中獲取一個值
- set:往store中設定一個值
- remove:從store中移除一個值(通過key)
它只有一個使用了Guava的預設實現:GuavaStore。如果需要,你可以提供你自己的一個Store。
8 釋出說明-向後相容性(Release notes - Backward compatibility)
此處略去。。。
9 認證流程-大圖片(Authentication flows - Big picture)
1) UI 認證(有狀態stateful/間接客戶端indirect client):
CAS特定的有狀態身份驗證流:
2) Web服務認證(無狀態stateless/直接客戶端direct client):
- Big picture:
10 自定義(customizations)
pac4j為各種需求提供了大量的元件,所以在進行定製之前,您應該仔細閱讀Clients, Authenticators 和 Authorizers頁面,以檢查已經提供的內容。
自定義認證器/授權器元件
確定你清楚的明白不同元件的角色是什麼:
- Client是一個完整的登入過程:間接地用於UI(IndirectClient),直接的用於web服務(DirectClient。它重定向到身份提供者(僅間接客戶端)),提取使用者憑證,驗證使用者憑證,併為經過身份驗證的使用者建立使用者配置檔案。
- RedirectActionBuilder會將使用者重定向到身份提供程式以便登入(間接客戶端)
- CredentialsExtractor從HTTP請求中提取使用者憑證(間接和直接的客戶端)
- Authenticator驗證使用者憑證(間接和直接客戶端)
- ProfileCreator為經過身份驗證的使用者(間接和直接客戶端)建立使用者配置檔案。
- Authorizer允許基於使用者配置檔案或web上下文訪問
- Matcher定義了安全性是否必須應用於web上下文
- 一個AuthorizationGenerator為給定的使用者概要檔案生成適當的角色和許可權。
重寫或建立新元件應該是簡單的。儘管如此,建立客戶端需要額外的努力,需要注意:
- 你需要了解你希望支援哪種身份驗證機制:它是用於UI(憑證只提供一次,身份驗證幾乎總是在外部身份提供程式中發生)或web服務(為每個請求傳遞憑證)
- 所有客戶端都應該實現IndirectClient介面,並定義適當的RedirectActionBuilder, CredentialsExtractor,Authenticator和ProfileCreator (以及可選的LogoutActionBuilder)。
- 它可能需要建立一個新的Credentials型別(如果它不是一個由TokenCredentials設計或UsernamePasswordCredentials設計的使用者名稱/密碼的簡單的字串)。這些新的憑證可能繼承受支援協議的基本憑證(如oauth憑據)
- 為新客戶端建立新概要通常是一種良好的實踐。(不管這個配置檔案是否有特定的資料),以便能夠區分所有使用者配置檔案。新的使用者配置檔案應該從協議支援的基本概要檔案繼承,比如OAuth20Profile。至少,它必須繼承自CommonProfile。身份提供者返回的資料可能需要被轉換(例如,一個單一的字串到Java列舉中),對於這一點,轉換器(類繼承了AttributeConverter的類)是必要的。轉換器和返回的使用者概要類都必須在ProfileDefinition中定義。
改變核心流:
覆蓋或建立新元件允許你在常規pac4j“過濾器”的定義邏輯的邊界內實現新的行為。然而,在某些情況下,這可能還不夠。因此,你可能決定打破流程,通過請求一些額外的操作來改變所提供的行為。這可以通過丟擲HttpAction(如任何異常)來實現,因為大多陣列件都允許這樣做。
例如:
public class ExampleAuthorizer implements Authorizer<CommonProfile> {
@Override
public boolean isAuthorized(WebContext context, List<CommonProfile> profiles) throws HttpAction {
if ("specificValue".equals(context.getRequestHeader("specificHeader")))
{
throw HttpAction.redirect("force redirection", context, "/message.html");
}
return true;
}
}
自定義web整合
pac4j實現嚴重依賴於WebContext和SessionStore來處理HTTP請求、響應和會話。這些元件的預設實現可能會被覆蓋或替換。除了預設的ProfileManager(用於儲存/恢復概要檔案)或GuavaStore(在快取中儲存資料)。在所有情況下,沒有什麼比檢視現有元件作為示例更好的了。請不要猶豫,在pac4j dev郵件列表上問任何問題。
11 第三方擴充套件
對於由第三方開發的pac4j,有一些擴充套件。擴充套件提供了核心pac4j發行版中不可用的特性,這些特性可能對特定的使用者有特定的需求是有用的。目前,以下副檔名是已知的:
IDC 擴充套件
IDC Extensions to PAC4J是IDC內部開發的一個專案,並以開源的形式釋出。它提供瞭如下模組:
- SAML客戶端的資料庫配置-這個模組允許你使用關係資料庫配置一系列SAML2客戶端,例如Oracle資料庫。你不需要改變你的PAC4J靜態配置(例如spring xml file)就能使應用的配置發生改變。你只需向資料庫表中新增一行或修改現有行,然後重新啟動你的應用程式即可。你也可以實現一個重新載入機制,允許你在不重啟應用程式的情況下進行配置更改。