在Spring 3.1中,對 Java Configuration (Java配置)的一般支持被添加到Spring框架中。自Spring Security 3.2以來,就有了Spring Security對Java配置的支持,用戶可以輕松配置Spring Security,而無需使用任何XML。
如果您熟悉第6章“安全命名空間配置”,那么您應該會發現它和安全Java配置支持之間有很多相似之處。
Spring Security提供了 lots of sample applications (許多示例應用程序),演示了Spring Security Java配置的使用。
5.1 Web Security Java Configuration(網絡安全Java配置)
第一步是創建我們的Spring安全Java配置。該配置創建了一個名為springSecurityFilterChain的Servlet過濾器,它負責應用程序中的所有安全性(保護應用程序URL、驗證提交的用戶名和密碼、重定向到登錄表單等)。您可以在下面找到Spring安全Java配置的最基本的例子:

1、這里真沒有配置多少內容,但它確實很有用。您可以找到以下功能的摘要:
2、在你的應用程序中對每個URL進行驗證
3、為你生成一個登陸表單
4、允許使用用戶名Usernameuser和密碼Passwordpassword使用驗證表單進行驗證。
5、允許用戶登出
6、CSRF attackCSPF攻擊防范
7、Session FixationSession保護
8、安全 Header 集成
HTTP Strict Transport Security對安全要求嚴格的HTTP傳輸安全
X-Content-Type-OptionsX-Content-Type-Options集成
緩存控制(稍后可以允許你緩存靜態資源)
X-XSS-ProtectionX-XSS-Protection集成
X-Frame-Options 集成防止點擊劫持Clickjacking
9、和以下 Servlet API 方法集成
HttpServletRequest#getRemoteUser()
HttpServletRequest.html#getUserPrincipal()
HttpServletRequest.html#isUserInRole(java.lang.String)
HttpServletRequest.html#login(java.lang.String, java.lang.String)
HttpServletRequest.html#logout()
5.1.1 AbstractSecurityWebApplicationInitializer(抽象安全網絡應用程序初始化器)
下一步是將springSecurityFilterChain注冊到war中。這可以在Servlet 3.0+環境中通過支持Spring的WebApplicationInitializer在Java配置中完成。不足為奇的是,Spring Security提供了一個基類AbstractSecurityWebApplicationInitializer(抽象安全性應用程序初始化器),它將確保為您注冊springSecurityFilterChain。我們使用AbstractSecurityWebApplicationInitializer(抽象安全性應用程序初始化器)的方式會有所不同,這取決於我們是否已經在使用Spring,或者Spring安全性是我們的應用程序中唯一的Spring組件。
第5.1.2節,“AbstractSecurityWebApplicationInitializer without Existing Spring”-如果您還沒有使用Spring,請使用這些說明。
第5.1.3節,“AbstractSecurityWebApplicationInitializer with Spring MVC”-如果您已經在使用Spring,請使用這些說明。
5.1.2 AbstractSecurityWebApplicationInitializer without Existing Spring(沒有現有Spring的抽象安全性應用程序初始化器)
如果您沒有使用Spring或Spring MVC,您將需要將網絡安全參數(WebSecurityConfig)傳入超類,以確保配置被采用。你可以在下面找到一個例子:

安全性應用程序初始化器(SecurityWebApplicationInitializer)將執行以下操作:
為應用程序中的每個網址自動注冊到springSecurityFilterChain過濾器。
添加一個加載網絡安全配置(WebSecurityConfig)的上下文加載器(ContextLoaderListener)。
5.1.3 AbstractSecurityWebApplicationInitializer with Spring MVC(有springMVC的抽象安全性應用程序初始化器 )
如果我們在應用程序的其他地方使用Spring,我們可能已經有了一個加載Spring配置的WebApplicationInitializer(web應用初始化器)。如果我們使用以前的配置,我們會得到一個錯誤。相反,我們應該在現有的應用上下文(ApplicationContext)中注冊Spring Security。例如,如果我們使用的是Spring MVC,我們的SecurityWebApplicationInitializer(安全web應用初始化器)看起來就像下面這樣:

這只會為應用程序中的每個網址注冊springSecurityFilterChain過濾器。之后,我們將確保WebSecurityConfig(web安全配置)加載到我們現有的應用程序初始化器中。例如,如果我們使用Spring MVC,它將被添加到getRootConfigClasses():

5.2 HttpSecurity(http安全)
到目前為止,我們的網絡安全配置只包含如何驗證用戶身份的信息。Spring Security如何知道我們想要要求所有用戶都經過身份驗證?Spring Security如何知道我們想要支持基於表單的身份驗證?原因是WebSecurityConfigurerAdapter(web安全配置適配器)在configure(HttpseSecurity http)方法中提供了一個默認配置,如下所示:

上面的默認配置:
確保我們應用中的所有請求都需要用戶被認證
允許用戶進行基於表單的認證
允許用戶使用HTTP基於驗證進行認證
你可以看到這個配置和下面的XML命名配置相似:

java配置使用and()方法相當於XML標簽的關閉。這允許我們繼續配置父對象。如果你閱讀代碼,它也是有意義的。我想配置授權請求,配置表單登錄和配置HTTP基本身份驗證。
然而,Java配置有不同的默認URL和參數。創建自定義登錄頁面時請記住這一點。結果是,我們的網址更加RESTful。此外,我們使用有助於防止信息泄露的Spring Security並不十分明顯。例如5.3所示:
5.3 Java Configuration and Form Login(java配置和表單登錄)
當提示您登錄時,您可能想知道登錄表單是從哪里來的,因為我們沒有提到任何HTML文件或JSP。由於Spring Security的默認配置沒有為登錄頁面明確設置URL,Spring Security會根據已啟用的功能自動生成一個,並使用處理提交的登錄的URL的標准值、用戶登錄后將被發送到的默認目標URL等等。
雖然自動生成的登錄頁面便於快速啟動和運行,但大多數應用程序都希望提供自己的登錄頁面。為此,我們可以更新我們的配置,如下所示:

1、指定登錄頁的路徑
2、我們必須允許所有用戶訪問我們的登錄頁(例如為驗證的用戶),這個formLogin().permitAll()方法允許基於表單登錄的所有的URL的所有用戶的訪問。
下面是我們當前配置的一個用JSPs實現的登錄頁面示例:
下面的登錄頁面代表我們當前的配置。如果某些默認值不能滿足我們的需求,我們可以輕松地更新我們的配置。

1、一個POST請求到/login用來驗證用戶
2、如果參數有error, 驗證嘗試失敗
3、如果請求蠶食logout存在則登出
4、登錄名參數必須被命名為username
5、密碼參數必須被命名為password
6、我們必須閱讀第18.4.3節“包含CSRF令牌”(Section 18.4.3, “Include the CSRF Token”)以了解更多信息,請閱讀第18章“跨站點請求偽造(CSRF)”(Chapter 18,Cross Site Request Forgery (CSRF))一節的參考資料
5.4 Authorize Requests(請求授權)
我們的例子中要求用戶進行身份驗證並且在我們應用程序的每個URL這樣做。我么你可以通過給http.authorizeRequests()添加多個子節點來指定多個定制需求到我們的URL。例如:

1、http.authorizeRequests()方法有多個子節點,每個macher按照他們的聲明順序執行。
2、我們指定任何用戶都可以通過訪問的多個URL模式。任何用戶都可以訪問URL以"/resources/", equals "/signup", 或者 "/about"開頭的URL。
3、以 "/admin/" 開頭的URL只能由擁有 "ROLE_ADMIN"角色的用戶訪問。請注意我們使用 hasRole 方法,沒有使用 "ROLE_" 前綴。
4、任何以"/db/" 開頭的URL需要用戶同時具有 "ROLE_ADMIN" 和 "ROLE_DBA"。和上面一樣我們的 hasRole 方法也沒有使用 "ROLE_" 前綴。
5、尚未匹配的任何URL要求用戶進行身份驗證。
5.5 Handling Logouts(登出處理)
當使用WebSecurityConfigurerAdapter, 注銷功能會自動應用。默認是訪問URL`/logout`將注銷登陸的用戶:
使HTTP Session 無效
清楚所有已經配置的 RememberMe 認證
清除SecurityContextHolder
重定向到 /login?logout

1、提供注銷支持,使用WebSecurityConfigurerAdapter會自動被應用。
2、設置觸發注銷操作的URL (默認是/logout). 如果CSRF內啟用(默認是啟用的)的話這個請求的方式被限定為POST。 請查閱相關信息JavaDoc相關信息.
3、注銷之后跳轉的URL。默認是/login?logout。具體查看JavaDoc文檔.
4、讓你設置定制的 LogoutSuccessHandler。如果指定了這個選項那么logoutSuccessUrl()的設置會被忽略。請查閱JavaDoc文檔.
5、指定是否在注銷時讓HttpSession無效。 默認設置為true。 在內部配置SecurityContextLogoutHandler,有關更多信息, 請查閱JavaDoc文檔.
6、添加一個LogoutHandler,默認情況下SecurityContextLogoutHandler會被添加為最后一個LogoutHandler。
7、允許指定在注銷成功時將移除的cookie。這是一個顯示的添加一個CookieClearingLogoutHandler的快捷方式。
注銷也可以通過XML命名空間進行配置,請參閱Spring Security XML命名空間相關文檔獲取更多細節logout element。
5.5.1 LogoutHandler(登出處理器)
通常,LogoutHandle的r實現類能夠參與到注銷處理中。它們將被調用來執行必要的清理。因此,他們不應該拋出異常。提供了各種實現方式:
PersistentTokenBasedRememberMeServices(基於持久令牌的記憶服務)
TokenBasedRememberMeServices(基於令牌的記憶服務)
CookieClearingLogoutHandler(Cookie清除注銷處理程序)
CsrfLogoutHandler(Csrf注銷處理程序)
SecurityContextLogoutHandler(安全上下文注銷處理程序)
詳情請參見第17.4節“記住我的界面和實現”。(Section 17.4, “Remember-Me Interfaces and Implementations”)
除了直接提供LogoutHandler實現,fluent應用編程接口還提供了快捷方式,這些快捷方式在封面下提供了各自的LogoutHandler實現。例如,deleteCookies()允許指定注銷成功時要刪除的一個或多個cookies的名稱。與添加CookieClearingLogoutHandler相比,這是一個快捷方式。
5.5.2 LogoutSuccessHandler(登出成功處理器)
LogoutSuccessHandler(登出成功處理器)被LogoutFilter(登出處理器)在成功注銷后調用,用來進行重定向或者轉發相應的目的地。注意這個接口與LogoutHandler(登出處理器)幾乎一樣,但是可以拋出異常。
下面是提供的一些實現:
SimpleUrlLogoutSuccessHandler(簡單URL登出成功處理器)
HttpStatusReturningLogoutSuccessHandler(http狀態返回登出成功處理器)
和前面提到的一樣,你不需要直接指定SimpleUrlLogoutSuccessHandler(簡單URL登出成功處理器)。而使用流式API接口通過設置logoutSuccessUrl(登出成功URL)快捷方式進行設置SimpleUrlLogoutSuccessHandler簡單URL登出成功處理器)。注銷成功 后將重定向到設置的URL地址。默認的地址是 /login?logout。
在REST API場景中HttpStatusReturningLogoutSuccessHandlerhttp狀態返回登出成功處理器)會進行一些有趣的改變。LogoutSuccessHandler(登出成功處理器)允許你設置一個返回給客戶端的HTTP狀態碼(默認返回200)來替換重定向到URL這個動作。
5.5.3 Further Logout-Related References(進一步登出相關參考)
Logout Handling(登出處理)
Testing Logout(登出測試)
HttpServletRequest.logout()(HTTPServletRequest登出)
Section 17.4, “Remember-Me Interfaces and Implementations”(Remember-Me接口和實現)
Logging Outin section CSRF Caveats (登出CSRF說明)
SectionSingle Logout(CAS protocol)(單點登出)
Documentation for thelogout elementin the Spring Security XML Namespace section(Spring安全XML命名空間部分中注銷元素的文檔)
5.6 Authentication(認證)
到目前為止,我們只看了最基本的身份驗證配置。讓我們看幾個稍微高級一點的配置身份驗證的選項。
5.6.1 In-Memory Authentication(內存中的身份驗證)
我們已經看到了一個單用戶配置到內存驗證的示例,下面是配置多個用戶的例子:

5.6.2 JDBC Authentication(jdbc認證)
你可以找一些更新來支持JDBC的驗證。下面的例子假設你已經在應用程序中定義好了DataSource, jdbc-javaconfig 示例提供了一個完整的基於JDBC的驗證。

5.6.3 LDAP Authentication(LDAP認證)
你可以找一些更新來支持LDAP的身份驗證, ldap-javaconfig 提供了一個完成的使用基於LDAP的身份驗證的示例。

上面的例子使用了下面的LDIF和一個嵌入的Apache DS LDAP實例。
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password
dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
5.6.4 AuthenticationProvider(認證提供商)
您可以通過一個自定義的AuthenticationProvider為bean定義自定義身份驗證。 例如, 下面這個例子假設自定義身份驗證SpringAuthenticationProvider實現了AuthenticationProvider:
僅當尚未填充AuthenticationManagerBuilder時,才使用此選項。

5.6.5 UserDetailsService(用戶詳細信息服務)
你可以通過一個自定義的UserDetailsService(用戶詳細信息服務)為bean定義自定義身份驗證。 例如,下面這個例子假設自定義身份驗證SpringDataUserDetailsService實現了UserDetailsService:
僅當尚未填充AuthenticationManagerBuilder(身份驗證管理器生成器)並且未定義AuthenticationProviderBean(身份驗證提供商)時,才使用此選項。

你也可以通過讓passwordencoder為bean自定義密碼如何編碼。 例如,如果你使用BCrypt,你可以添加一個bean定義如下圖所示:

5.6.6 LDAP Authentication(LDAP身份認證)
5.7 Multiple HttpSecurity(多個HttpSecurity)
我們可以配置多個http安全實例,就像我們可以有多個< http >塊一樣。關鍵是多次擴展WebSecurityConfigurerAdapter。例如,下面是一個為以/api/開頭的網址進行不同配置的示例。

配置正常的驗證。
1、創建一個WebSecurityConfigurerAdapter,包含一個@Order注解,用來指定個哪一個WebSecurityConfigurerAdapter更優先。
2、http.antMatcher指出,這個HttpSecurity只應用到以/api/開頭的URL上。
3、創建另外一個WebSecurityConfigurerAdapter實例。用於不以/api/開頭的URL,這個配置的順序在ApiWebSecurityConfigurationAdapter之后,因為他沒有指定@Order值為1(沒有指定@Order默認會被放到最后).
5.8 Method Security(方法安全性)
從版本2.0開始,Spring Security已經大大改進了對為服務層方法增加安全性的支持。它支持JSR-250注釋安全性以及框架的原始@Secured注釋。從3.0開始,您還可以使用新的基於表達式(expression-based annotations.)的注釋。您可以對單個bean應用安全性,使用intercept-methods元素來修飾bean聲明,或者使用AspectJ風格的切入點來保護整個服務層中的多個bean。
5.8.1 EnableGlobalMethodSecurity(啟用全局方法安全性)
我們可以在任何@Configuration實例上使用@EnableGlobalMethodSecurity注釋來啟用基於注釋的安全性。例如,下面將啟用Spring Security的@Secured注釋。

向方法(在類或接口上)添加注釋會相應地限制對該方法的訪問。Spring Security的原生注釋支持為該方法定義了一組屬性。這些將被傳遞給AccessDecisionManager(訪問決策管理器),以便它做出實際的決策:

使用如下代碼啟用JSR-250注解的支持

這些都是基於標准的,允許應用簡單的基於角色的約束,但不具備Spring Security的原生注釋。要使用新的基於表達式的語法,您應該使用

等效的Java代碼是

5.8.2 GlobalMethodSecurityConfiguration(全局方法安全性配置)
有時您可能需要執行比@EnableGlobalMethodSecurity注釋允許的操作更復雜的操作。對於這些實例,您可以擴展GlobalMethodsecurityConfiguration(全局方法安全性配置),確保您的子類中存在@EnableGlobalMethodSecurity注釋。例如,如果您想提供一個自定義的MethodSecurityExpressionHandler(方法安全表達式處理程序),您可以使用以下配置:

關於可以被重寫的方法的更多信息,請參考GlobalMethodSecurityConfiguration(全局方法安全性配置)的java文檔。
5.9 Post Processing Configured Objects(后置處理配置的對象)
Spring Security的Java配置沒有公開它配置的每個對象的每個屬性。這簡化了大多數用戶的配置。畢竟,如果每個屬性都公開了,用戶可以使用標准的bean配置。
雖然有充分的理由不直接公開每個屬性,但是用戶可能仍然需要更高級的配置選項。為了解決這個問題,Spring Security引入了ObjectPostProcessor(后置對象處理器)的概念,它可以用來修改或替換許多由Java配置創建的對象實例。例如,如果您想在FilterSecurityInterceptor(安全過濾攔截器)上配置FilterSecurityPublishauthorizationSuccess(安全過濾發布授權成功)屬性,可以使用以下方法:

5.10 Custom DSLs(自定義DSLs)
您可以在Spring Security中提供自己的定制DSL。例如,你可能有這樣的東西:

這實際上是像HttpSecurity.authorizeRequests()這樣的方法是來實現的。
自定義DSL可以這樣使用:

代碼按以下順序調用:
1、調用“配置”的配置方法中的代碼
2、調用了“MyCustomDsl的初始化方法”中的代碼
3、調用了“MyCustomDsl的配置方法”中的代碼
如果需要,您可以使用SpringFactories讓WebSecurityConfiguerAdapter默認添加MyCustomDsl。例如,您可以在類路徑上創建一個名為META-INF/spring.factories的資源,其內容如下:
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
希望禁用默認值的用戶可以明確地這樣做。
