目錄
1.1 ???? 用戶(hù)信息從數(shù)據(jù)庫(kù)獲取
1.1.1 ??? 使用 jdbc-user-service 獲取
1.2.1 ??? 使用內(nèi)置的 PasswordEncoder
1.2.2 ??? 使用自定義的 PasswordEncoder
?
?????? 認(rèn)證是由 AuthenticationManager 來(lái)管理的,但是真正進(jìn)行認(rèn)證的是 AuthenticationManager 中定義的 AuthenticationProvider 。 AuthenticationManager 中可以定義有多個(gè) AuthenticationProvider 。當(dāng)我們使用 authentication-provider 元素來(lái)定義一個(gè) AuthenticationProvider 時(shí),如果沒(méi)有指定對(duì)應(yīng)關(guān)聯(lián)的 AuthenticationProvider 對(duì)象, Spring Security 默認(rèn)會(huì)使用 DaoAuthenticationProvider 。 DaoAuthenticationProvider 在進(jìn)行認(rèn)證的時(shí)候需要一個(gè) UserDetailsService 來(lái)獲取用戶(hù)的信息 UserDetails ,其中包括用戶(hù)名、密碼和所擁有的權(quán)限等。所以如果我們需要改變認(rèn)證的方式,我們可以實(shí)現(xiàn)自己的 AuthenticationProvider ;如果需要改變認(rèn)證的用戶(hù)信息來(lái)源,我們可以實(shí)現(xiàn) UserDetailsService 。
?
?????? 實(shí)現(xiàn)了自己的 AuthenticationProvider 之后,我們可以在配置文件中這樣配置來(lái)使用我們自己的 AuthenticationProvider 。其中 myAuthenticationProvider 就是我們自己的 AuthenticationProvider 實(shí)現(xiàn)類(lèi)對(duì)應(yīng)的 bean 。
?? < security:authentication-manager >
????? < security:authentication-provider ref = "myAuthenticationProvider" />
?? </ security:authentication-manager >
?
?????? 實(shí)現(xiàn)了自己的 UserDetailsService 之后,我們可以在配置文件中這樣配置來(lái)使用我們自己的 UserDetailsService 。其中的 myUserDetailsService 就是我們自己的 UserDetailsService 實(shí)現(xiàn)類(lèi)對(duì)應(yīng)的 bean 。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "myUserDetailsService" />
?? </ security:authentication-manager >
?
1.1 ???? 用戶(hù)信息從數(shù)據(jù)庫(kù)獲取
?????? 通常我們的用戶(hù)信息都不會(huì)向第一節(jié)示例中那樣簡(jiǎn)單的寫(xiě)在配置文件中,而是從其它存儲(chǔ)位置獲取,比如數(shù)據(jù)庫(kù)。根據(jù)之前的介紹我們知道用戶(hù)信息是通過(guò) UserDetailsService 獲取的,要從數(shù)據(jù)庫(kù)獲取用戶(hù)信息,我們就需要實(shí)現(xiàn)自己的 UserDetailsService 。幸運(yùn)的是像這種常用的方式 Spring Security 已經(jīng)為我們做了實(shí)現(xiàn)了。
?
1.1.1?? 使用jdbc-user-service獲取
?????? 在 Spring Security 的命名空間中在 authentication-provider 下定義了一個(gè) jdbc-user-service 元素,通過(guò)該元素我們可以定義一個(gè)從數(shù)據(jù)庫(kù)獲取 UserDetails 的 UserDetailsService 。 jdbc-user-service 需要接收一個(gè)數(shù)據(jù)源的引用。
?? < security:authentication-manager >
????? < security:authentication-provider >
???????? < security:jdbc-user-service data-source-ref = "dataSource" /> ??????
????? </ security:authentication-provider >
?? </ security:authentication-manager >
?
?????? 上述配置中 dataSource 是對(duì)應(yīng)數(shù)據(jù)源配置的 bean 引用。使用此種方式需要我們的數(shù)據(jù)庫(kù)擁有如下表和表結(jié)構(gòu)。
?????? 這是因?yàn)槟J(rèn)情況下 jdbc-user-service 將使用 SQL 語(yǔ)句“ select username, password, enabled from users where username = ? ”來(lái)獲取用戶(hù)信息;使用 SQL 語(yǔ)句“ select username, authority from authorities where username = ? ”來(lái)獲取用戶(hù)對(duì)應(yīng)的權(quán)限;使用 SQL 語(yǔ)句“ select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id ”來(lái)獲取用戶(hù)所屬組的權(quán)限。需要注意的是 jdbc-user-service 定義是不支持用戶(hù)組權(quán)限的,所以使用 jdbc-user-service 時(shí)用戶(hù)組相關(guān)表也是可以不定義的。如果需要使用用戶(hù)組權(quán)限請(qǐng)使用 JdbcDaoImpl ,這個(gè)在后文后講到。
?????? 當(dāng)然這只是默認(rèn)配置及默認(rèn)的表結(jié)構(gòu)。如果我們的表名或者表結(jié)構(gòu)跟 Spring Security 默認(rèn)的不一樣,我們可以通過(guò)以下幾個(gè)屬性來(lái)定義我們自己查詢(xún)用戶(hù)信息、用戶(hù)權(quán)限和用戶(hù)組權(quán)限的 SQL 。
|
屬性名 |
說(shuō)明 |
|
users-by-username-query |
指定查詢(xún)用戶(hù)信息的 SQL |
|
authorities-by-username-query |
指定查詢(xún)用戶(hù)權(quán)限的 SQL |
|
group-authorities-by-username-query |
指定查詢(xún)用戶(hù)組權(quán)限的 SQL |
?
?????? 假設(shè)我們的用戶(hù)表是 t_user ,而不是默認(rèn)的 users ,則我們可以通過(guò)屬性 users-by-username-query 來(lái)指定查詢(xún)用戶(hù)信息的時(shí)候是從用戶(hù)表 t_user 查詢(xún)。
?? < security:authentication-manager >
????? < security:authentication-provider >
???????? < security:jdbc-user-service
??????????? data-source-ref = "dataSource"
??????????? users-by-username-query = "select username, password, enabled from t_user where username = ?" />
????? </ security:authentication-provider >
?? </ security:authentication-manager >
?
role-prefix 屬性
?????? jdbc-user-service 還有一個(gè)屬性 role-prefix 可以用來(lái)指定角色的前綴。這是什么意思呢?這表示我們從庫(kù)里面查詢(xún)出來(lái)的權(quán)限需要加上什么樣的前綴。舉個(gè)例子,假設(shè)我們庫(kù)里面存放的權(quán)限都是“ USER ”,而我們指定了某個(gè) URL 的訪(fǎng)問(wèn)權(quán)限 access=”ROLE_USER” ,顯然這是不匹配的, Spring Security 不會(huì)給我們放行,通過(guò)指定 jdbc-user-service 的 role-prefix=”ROLE_” 之后就會(huì)滿(mǎn)足了。當(dāng) role-prefix 的值為“ none ”時(shí)表示沒(méi)有前綴,當(dāng)然默認(rèn)也是沒(méi)有的。
?
1.1.2?? 直接使用JdbcDaoImpl
?????? JdbcDaoImpl 是 UserDetailsService 的一個(gè)實(shí)現(xiàn)。其用法和 jdbc-user-service 類(lèi)似,只是我們需要把它定義為一個(gè) bean ,然后通過(guò) authentication-provider 的 user-service-ref 進(jìn)行引用。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" />
?? </ security:authentication-manager >
??
?? < bean id = "userDetailsService" class = "org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl" >
????? < property name = "dataSource" ref = "dataSource" />
?? </ bean >
?
?????? 如你所見(jiàn), JdbcDaoImpl 同樣需要一個(gè) dataSource 的引用。如果就是上面這樣配置的話(huà)我們數(shù)據(jù)庫(kù)表結(jié)構(gòu)也需要是標(biāo)準(zhǔn)的表結(jié)構(gòu)。當(dāng)然,如果我們的表結(jié)構(gòu)和標(biāo)準(zhǔn)的不一樣,可以通過(guò) usersByUsernameQuery 、 authoritiesByUsernameQuery 和 groupAuthoritiesByUsernameQuery 屬性來(lái)指定對(duì)應(yīng)的查詢(xún) SQL 。
用戶(hù)權(quán)限和用戶(hù)組權(quán)限
?????? JdbcDaoImpl 使用 enableAuthorities 和 enableGroups 兩個(gè)屬性來(lái)控制權(quán)限的啟用。默認(rèn)啟用的是 enableAuthorities ,即用戶(hù)權(quán)限,而 enableGroups 默認(rèn)是不啟用的。如果需要啟用用戶(hù)組權(quán)限,需要指定 enableGroups 屬性值為 true 。當(dāng)然這兩種權(quán)限是可以同時(shí)啟用的。需要注意的是使用 jdbc-user-service 定義的 UserDetailsService 是不支持用戶(hù)組權(quán)限的,如果需要支持用戶(hù)組權(quán)限的話(huà)需要我們使用 JdbcDaoImpl 。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" />
?? </ security:authentication-manager >
??
?? < bean id = "userDetailsService" class = "org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl" >
????? < property name = "dataSource" ref = "dataSource" />
????? < property name = "enableGroups" value = "true" />
?? </ bean >
?
1.2 ???? PasswordEncoder
1.2.1?? 使用內(nèi)置的PasswordEncoder
?????? 通常我們保存的密碼都不會(huì)像之前介紹的那樣,保存的明文,而是加密之后的結(jié)果。為此,我們的 AuthenticationProvider 在做認(rèn)證時(shí)也需要將傳遞的明文密碼使用對(duì)應(yīng)的算法加密后再與保存好的密碼做比較。 Spring Security 對(duì)這方面也有支持。通過(guò)在 authentication-provider 下定義一個(gè) password-encoder 我們可以定義當(dāng)前 AuthenticationProvider 需要在進(jìn)行認(rèn)證時(shí)需要使用的 password-encoder 。 password-encoder 是一個(gè) PasswordEncoder 的實(shí)例,我們可以直接使用它,如:
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" >
???????? < security:password-encoder hash = "md5" />
????? </ security:authentication-provider >
?? </ security:authentication-manager >
?
?????? 其屬性 hash 表示我們將用來(lái)進(jìn)行加密的哈希算法,系統(tǒng)已經(jīng)為我們實(shí)現(xiàn)的有 plaintext 、 sha 、 sha-256 、 md4 、 md5 、 {sha} 和 {ssha} 。它們對(duì)應(yīng)的 PasswordEncoder 實(shí)現(xiàn)類(lèi)如下:
|
加密算法 |
PasswordEncoder 實(shí)現(xiàn)類(lèi) |
|
plaintext |
PlaintextPasswordEncoder |
|
sha |
ShaPasswordEncoder |
|
sha-256 |
ShaPasswordEncoder ,使用時(shí) new ShaPasswordEncoder(256) |
|
md4 |
Md4PasswordEncoder |
|
md5 |
Md5PasswordEncoder |
|
{sha} |
LdapShaPasswordEncoder |
|
{ssha} |
LdapShaPasswordEncoder |
?
使用 BASE64 編碼加密后的密碼
?????? 此外,使用 password-encoder 時(shí)我們還可以指定一個(gè)屬性 base64 ,表示是否需要對(duì)加密后的密碼使用 BASE64 進(jìn)行編碼,默認(rèn)是 false 。如果需要?jiǎng)t設(shè)為 true 。
<security:password-encoder hash= "md5" base64= "true" />
?
加密時(shí)使用 salt
?????? 加密時(shí)使用 salt 也是很常見(jiàn)的需求, Spring Security 內(nèi)置的 password-encoder 也對(duì)它有支持。通過(guò) password-encoder 元素下的子元素 salt-source ,我們可以指定當(dāng)前 PasswordEncoder 需要使用的 salt 。這個(gè) salt 可以是一個(gè)常量,也可以是當(dāng)前 UserDetails 的某一個(gè)屬性,還可以通過(guò)實(shí)現(xiàn) SaltSource 接口實(shí)現(xiàn)自己的獲取 salt 的邏輯, SaltSource 中只定義了如下一個(gè)方法。
public Object getSalt(UserDetails user);
?
?????? 下面來(lái)看幾個(gè)使用 salt-source 的示例。
?????? ( 1 )下面的配置將使用常量“ abc ”作為 salt 。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" >
???????? < security:password-encoder hash = "md5" base64 = "true" >
????? ????? < security:salt-source system-wide = "abc" />
???????? </ security:password-encoder >
????? </ security:authentication-provider >
?? </ security:authentication-manager >
?
?????? ( 2 )下面的配置將使用 UserDetails 的 username 作為 salt 。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" >
???????? < security:password-encoder hash = "md5" base64 = "true" >
??????????? < security:salt-source user-property = "username" />
???????? </ security:password-encoder >
????? </ security:authentication-provider >
?? </ security:authentication-manager >
?
?????? ( 3 )下面的配置將使用自己實(shí)現(xiàn)的 SaltSource 獲取 salt 。其中 mySaltSource 就是 SaltSource 實(shí)現(xiàn)類(lèi)對(duì)應(yīng)的 bean 的引用。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" >
???????? < security:password-encoder hash = "md5" base64 = "true" >
??????????? < security:salt-source ref = "mySaltSource" />
???????? </ security:password-encoder >
????? </ security:authentication-provider >
?? </ security:authentication-manager >
?
?????? 需要注意的是 AuthenticationProvider 進(jìn)行認(rèn)證時(shí)所使用的 PasswordEncoder ,包括它們的算法和規(guī)則都應(yīng)當(dāng)與我們保存用戶(hù)密碼時(shí)是一致的。也就是說(shuō)如果 AuthenticationProvider 使用 Md5PasswordEncoder 進(jìn)行認(rèn)證,我們?cè)诒4嬗脩?hù)密碼時(shí)也需要使用 Md5PasswordEncoder ;如果 AuthenticationProvider 在認(rèn)證時(shí)使用了 username 作為 salt ,那么我們?cè)诒4嬗脩?hù)密碼時(shí)也需要使用 username 作為 salt 。如:
?? Md5PasswordEncoder encoder = new Md5PasswordEncoder();
?? encoder.setEncodeHashAsBase64( true );
?? System. out .println(encoder.encodePassword( "user" , "user" ));
?
1.2.2?? 使用自定義的PasswordEncoder
?????? 除了通過(guò) password-encoder 使用 Spring Security 已經(jīng)為我們實(shí)現(xiàn)了的 PasswordEncoder 之外,我們也可以實(shí)現(xiàn)自己的 PasswordEncoder ,然后通過(guò) password-encoder 的 ref 屬性關(guān)聯(lián)到我們自己實(shí)現(xiàn)的 PasswordEncoder 對(duì)應(yīng)的 bean 對(duì)象。
?? < security:authentication-manager >
????? < security:authentication-provider user-service-ref = "userDetailsService" >
???????? < security:password-encoder ref = "passwordEncoder" />
????? </ security:authentication-provider >
?? </ security:authentication-manager >
??
?? < bean id = "passwordEncoder" class = "com.xxx.MyPasswordEncoder" />
?
?????? 在 Spring Security 內(nèi)部定義有兩種類(lèi)型的 PasswordEncoder ,分別是 org.springframework.security.authentication.encoding.PasswordEncoder 和 org.springframework.security.crypto.password.PasswordEncoder 。直接通過(guò) password-encoder 元素的 hash 屬性指定使用內(nèi)置的 PasswordEncoder 都是基于 org.springframework.security.authentication.encoding.PasswordEncoder 的實(shí)現(xiàn),然而它現(xiàn)在已經(jīng)被廢棄了, Spring Security 推薦我們使用 org.springframework.security.crypto.password.PasswordEncoder ,它的設(shè)計(jì)理念是為了使用隨機(jī)生成的 salt 。關(guān)于后者 Spring Security 也已經(jīng)提供了幾個(gè)實(shí)現(xiàn)類(lèi),更多信息請(qǐng)查看 Spring Security 的 API 文檔。我們?cè)谕ㄟ^(guò) password-encoder 使用自定義的 PasswordEncoder 時(shí)兩種 PasswordEncoder 的實(shí)現(xiàn)類(lèi)都是支持的。
?
(注:本文是基于 Spring Security3.1.6 所寫(xiě))
(注:原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處。原文地址: http://haohaoxuexi.iteye.com/blog/2157769 )
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

