在前一篇,我已經介紹了Spring Security Java配置,也概括的介紹了一下這個項目方方面面。在這篇文章中,我們來看一看一個簡單的基於web security配置的例子。之后我們再來作更多的個人定制。
Hello Web Security
在這個部分,我們對一個基於web的security作一些基本的配置。可以分成四個部分:
- 更新依賴 – 我們已經在前一篇文章中用Maven進行了示范
- 進行Spring Security配置 – 這個例子中,我們采用WebSecurityConfigurerAdapter
- 確保Spring Security配置已經被加載了 – 我們采用AbstractAnnotationConfigDispatcherServletInitializer
- 配置springSecurityFilterChain – 我們采用AbstractSecurityWebApplicationInitializer
WebSecurityConfigurerAdapter
@EnableWebSecurity注解以及WebSecurityConfigurerAdapter一起配合提供基於web的security。繼承了WebSecurityConfigurerAdapter之后,再加上幾行代碼,我們就能實現以下的功能:
- 要求用戶在進入你的應用的任何URL之前都進行驗證
- 創建一個用戶名是“user”,密碼是“password”,角色是“ROLE_USER”的用戶
- 啟用HTTP Basic和基於表單的驗證
- Spring Security將會自動生成一個登陸頁面和登出成功頁面
1
2
3
4
5
6
7
8
9
10
|
@Configuration
@EnableWebSecurity
public
class
HelloWebSecurityConfiguration
extends
WebSecurityConfigurerAdapter {
@Override
protected
void
registerAuthentication(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication()
.withUser(
"user"
).password(
"password"
).roles(
"USER"
);
}
}
|
作為參考,我們在這里也給出相似的XML配置,不過有幾個特殊配置:
- Spring Security會生成一個登陸頁面,驗證失敗頁面和登出成功頁面
- login-processing-url僅僅處理HTTP POST
- login-page僅僅通過HTTP GET進入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<
http
use-expressions
=
"true"
>
<
intercept-url
pattern
=
"/**"
access
=
"authenticated"
/>
<
logout
logout-success-url
=
"/login?logout"
logout-url
=
"/logout"
/>
<
form-login
authentication-failure-url
=
"/login?error"
login-page
=
"/login"
login-processing-url
=
"/login"
password-parameter
=
"password"
username-parameter
=
"username"
/>
</
http
>
<
authentication-manager
>
<
authentication-provider
>
<
user-service
>
<
user
name
=
"user"
password
=
"password"
authorities
=
"ROLE_USER"
/>
</
user-service
>
</
authentication-provider
>
</
authentication-manager
>
|
AbstractAnnotationConfigDispatcherServletInitializer
下一步就是保證ApplicationContext包含我們剛剛定義的HelloWebSecurityConfiguration。有幾種方法都可行,我們這里使用Spring的AbstractAnnotationConfigDispatcherServletInitializer:
1
2
3
4
5
6
7
8
9
|
public
class
SpringWebMvcInitializer
extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected
Class[] getRootConfigClasses() {
return
new
Class[] { HelloWebSecurityConfiguration.
class
};
}
...
}
|
Spring Security通常在web.xml中包含下面幾行代碼進行初始化:
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<
listener
>
<
listener-class
>
org.springframework.web.context.ContextLoaderListener
</
listener-class
>
</
listener
>
<!-- Load all Spring XML configuration including our security.xml file -->
<
context-param
>
<
param-name
>contextConfigLocation</
param-name
>
<
param-value
>/WEB-INF/spring/*.xml</
param-value
>
</
context-param
>
|
AbstractSecurity WebApplicationInitializer
最后一步,我們需要對springSecurityFilterChain定義映射路徑。我們很容易通過繼承AbstractSecurityWebApplicationInitializer實現,並可以有選擇的通過覆蓋方法來定制映射。
下面是最基本的配置,它可以接受默認的映射路徑,springSecurityFilterChain具有以下的特性:
- springSecurityFilterChain映射到了”/*”
- springSecurityFilterChain使用ERROR和REQUEST分派類型(dispatch type)
- springSecurityFilterChain插入到其它已經配置的servlet過濾器映射(servlet Filter mapping)之前
1
2
3
|
public
class
SecurityWebApplicationInitializer
extends
AbstractSecurityWebApplicationInitializer {
}
|
上面的代碼等同於將這幾行代碼放在web.xml中:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<
filter
>
<
filter-name
>springSecurityFilterChain</
filter-name
>
<
filter-class
>
org.springframework.web.filter.DelegatingFilterProxy
</
filter-class
>
</
filter
>
<
filter-mapping
>
<
filter-name
>springSecurityFilterChain</
filter-name
>
<
url-pattern
>/*</
url-pattern
>
<
dispatcher
>ERROR</
dispatcher
>
<
dispatcher
>REQUEST</
dispatcher
>
</
filter-mapping
>
|
WebApplicationInitializer的次序
在AbstractSecurityWebApplicationInitializer啟動之后再加入的servlet過濾器映射,它們有可能會加在springSecurityFilterChain之前。除非這個應用不需要安全驗證,否則springSecurityFilterChain需要放在其它所有的過濾器映射之前。@Order可以保證任何WebApplicationInitializer都使用特定的順序加載。
CustomWebSecurityConfigurerAdapter
這個HelloWebSecurityConfiguration范例,為我們很好的展示了Spring Security Java配置是如何工作的。讓我們來看看更多定制的配置吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@EnableWebSecurity
@Configuration
public
class
CustomWebSecurityConfigurerAdapter
extends
WebSecurityConfigurerAdapter {
@Override
protected
void
registerAuthentication(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser(
"user"
)
// #1
.password(
"password"
)
.roles(
"USER"
)
.and()
.withUser(
"admin"
)
// #2
.password(
"password"
)
.roles(
"ADMIN"
,
"USER"
);
}
@Override
public
void
configure(WebSecurity web)
throws
Exception {
web
.ignoring()
.antMatchers(
"/resources/**"
);
// #3
}
@Override
protected
void
configure(HttpSecurity http)
throws
Exception {
http
.authorizeUrls()
.antMatchers(
"/signup"
,
"/about"
).permitAll()
// #4
.antMatchers(
"/admin/**"
).hasRole(
"ADMIN"
)
// #6
.anyRequest().authenticated()
// #7
.and()
.formLogin()
// #8
.loginUrl(
"/login"
)
// #9
.permitAll();
// #5
}
}
|
我們也需要更新AbstractAnnotationConfigDispatcherServletInitializer
,這樣CustomWebSecurityConfigurerAdapter
可以實現以下功能:
- #1 可以在內存中的驗證(memory authentication)叫作”user”的用戶
- #2 可以在內存中的驗證(memory authentication)叫作”admin”的管理員用戶
- #3 忽略任何以”/resources/”開頭的請求,這和在XML配置http@security=none的效果一樣
- #4 任何人(包括沒有經過驗證的)都可以訪問”/signup”和”/about”
- #5 任何人(包括沒有經過驗證的)都可以訪問”/login”和”/login?error”。permitAll()是指用戶可以訪問formLogin()相關的任何URL。
- #6 “/admin/”開頭的URL必須要是管理員用戶,譬如”admin”用戶
- #7 所有其他的URL都需要用戶進行驗證
- #8 使用Java配置默認值設置了基於表單的驗證。使用POST提交到”/login”時,需要用”username”和”password”進行驗證。
- #9 注明了登陸頁面,意味着用GET訪問”/login”時,顯示登陸頁面
下面的XML配置和上面的Java配置類似:
Java配置和XML命名空間的相同之處
在看過了更復雜的例子之后,你可能已經找到了一些XML命名空間和Java配置的相似之處。我在這里說明幾條有用的信息:
- HttpSecurity和http命名空間類似。它可以對於某一部分請求進行特別配置。要看個完整的配置實例,詳見SampleMultiHttpSecurityConfig
- WebSecurity和Security的命名空間的元素很類似,后者是針對web的,不需要父節點(security=none, debug等等)。可以對整個web security進行配置。
- WebSecurityConfigurerAdapter方便我們定制WebSecurity和HttpSecurity。我們可以對WebSecurityConfigurerAdapter進行多次繼承,以實現不同的http行為。詳細的實例參見SampleMultiHttpSecurityConfig
- 我們以上的Java配置代碼作了代碼格式化,所以易於閱讀。“and()”類似於XML中結束一個元素的結束符。
Java配置和XML命名空間的不同之處
你已經意識到了XML和Java配置的不同之處
- 當你在”#1″和“#2”中創建用戶的時候,我們並沒有設置為”ROLE_“前綴,而我們在XML設置成了“ROLE_USER”。因為這是個管理,”roles()”方法會自動添加”ROLE_“。如果你不想要”ROLE_“,你可以使用”authoritites()”方法。
- Java配置有一些不同的默認URL和參數。當要創建自定義的登陸頁面的時候要將這一條牢記在心。默認的URL使我們的URL更加RESTful。另外,使用Spring Security可以幫我避免信息泄露)。例如:
- GET訪問
/login
登陸頁面,而不是訪問/spring_security_login
- POST訪問
/login,而不是/j_spring_security_check
- 用戶名默認為parameter,而不是j_username
- 密碼默認是password,而不是j_password
- GET訪問
- Java配置可以更容易將多個請求映射到同樣的角色上。#4就將兩個URL作了映射,以便所有人都可以訪問
- Java移除了多余的代碼。例如,在XML中我們不得不在
form-login
和intercept-url
中重復兩次”/login”,而在Java配置中,我們靠#5就輕易做到了讓用戶都能訪問到和formLogin()相關的URL。 - #6映射HTTP請求的時候,我們使用了“hasRole()”方法,我們也沒有添加”ROLE_”前綴,而在XML中我們則添加了。這也是我們應該知道的慣例:”hasRole()”會自動添加”ROLE_”前綴。如果你不想要“ROLE_”前綴,你可以使用”access()”方法。
更多的示例
我們還提供了更多的示例,你應該想躍躍欲試了吧:
從XML命名空間到Java配置
如果你覺得從XML轉變成Java配置有一定困難,你可以先看看這些測試。這些測試中,XML元素的名稱以”Namespace”開頭,中間是XML元素的名稱,然后以”Tests”結尾。例如,你想學習如何將http元素轉換成Java配置,你可以看看NamespaceHttpTests;你如果想學習如何將remember-me命名空間轉換成Java配置,參見NamespaceRememberMeTests
歡迎反饋
如果你發現了bug,或者覺得有什么地方值得改進,請你不要猶豫,給我們留言!我們希望聽到你的想法,以便在大部分人獲得代碼之前,我們便確保代碼的正確性。很早的嘗試新功能是一種簡單有效的回饋社區的方法,這樣做的好處就是能幫助你獲得你希望獲得的功能。
請到“Java Config”目錄下的Spring Security JIRA記錄下任何問題。記錄了一個JIRA之后,我們希望(當然並不是必須的)你在pull request中提交你的代碼。你可以在貢獻者指引中閱讀更詳細的步驟。如果你有任何不清楚的,請使用Spring Security論壇或者Stack Overflow,並使用”spring-security”標簽(我會一直查看這個標簽)。如果你針對這個博客有任何意見,也請留言。使用合適的工具對每個人來說都會帶來便利。
結論
你可能已經對基於web的security的Java配置已經有了一定的認識了。下一篇中,我們將會帶你來看下如何用Java配置來配置基於method的security。
原文鏈接: Springsource 翻譯: ImportNew.com - 唐小娟
譯文鏈接: http://www.importnew.com/5641.html
[ 轉載請保留原文出處、譯者和譯文鏈接。]