9.1 Spring Security簡介
Spring Security 是基於Spring AOP和Servlet規範中的Filter實現的安全框架,能夠在Web請求級別和方法調用級別處理身份認證和授權。
Spring Security 從兩個角度來解決安全性問題。它使用Servlet 規範中的Filter的保護Web請求並限制URL級別的訪問。Spring Security 通過使用Spring AOP 保護方法調用——藉助於對象代理和使用通知,能夠確保只有具備適當權限的用戶才能訪問安全保護的方法。
9.1.1 Speing Security的模塊
9.1.2 過濾Web請求
通過web.xml或者AbstractSecurityWebApplicationInitializer的子類配置 DelegatingFilterProxy,都會攔截髮往應用中的請求,並將請求委託給ID爲 springSecurityFilterChain 的bean。
9.1.3 簡單的安全性配置
@Configuration
@EnableWebSecurity // ->啓用web應用的安全性功能
public class SecurityConfig extends WebSecurityConfigurerAdapter{
}
使用SpringMVC開發,則用 @EnableWebMvcSecurity替代@EnableWebSecurity,通過重載 WebSecurityConfigurerAdapter的是哪個configure()方法來配置Web安全性。
方法 | 描述 |
---|---|
configure(WebSecurity) | 通過重載,配置Spring Security的Filter鏈 |
configure(HttpSecurity) | 通過重載,配置如何通過攔截器保護請求 |
configure(AuthenticationManagerBuilder) | 通過重載,配置user-detail服務 |
默認的configure(HttpSecurity) 代碼
prorected void configure(HttpSecurity http) throws Exception{
http
.authrizeRequests()
.anyRequest().authenicated() // 所有進入應用的HTTP請求都要驗證
.and()
.formLogin().add() // 支持基於表單的登錄以及HTTP Basic方式的認證
.httpBasic();
}
//單沒有用戶存儲支撐認證過程,沒有用戶存儲,實際上就等於沒有用戶,沒人能登錄成功
- 配置用戶存儲
- 指定哪些請求需要認證,哪些請求不需要認證,以及所需要的權限;
- 提供一個自定義的登錄界面,替代原來簡單的默認登錄頁。
9.2 選擇查詢用戶信息的服務
9.2.1 基於內存的用戶存儲
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication() //啓用內存用戶存儲
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER","ADMIN");
}
}
配置用戶詳細信息的方法
方法 | 描述 |
---|---|
accountExpired(boolean) | 定義賬號是否已經過期 |
accountLocked(boolean) | 定義賬號是否已經鎖定 |
and() | 用來連接配置 |
authorities(GrantedAuthority…) | 授予某個用戶一項或多項權限 |
authorities(List<? extendsGrantedAuthority…> ) | 授予某個用戶一項或多項權限 |
authorities(String…) | 授予某個用戶一項或多項權限 |
credentialsExpired(boolean) | 定義憑證是否已經過期 |
disabled(boolean) | 定義賬號是否已被禁用 |
password(String) | 定義用戶的密碼 |
roles(String…) | 授予某個用戶一項或多項角色 |
以上方法在學習實現過程中,發現了已經有幾個地方出現了更改
1.@EnbaleWebMvcSecurity已被啓用 -> 改爲@EnableWebSecurity
2.執行測試之後出現異常
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:250) ~[spring-security-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:198) ~[spring-security-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$LazyPasswordEncoder.matches(WebSecurityConfigurerAdapter.java:592) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
測試成功的代碼
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) //啓用內存用戶存儲
.withUser("user").password(new BCryptPasswordEncoder().encode("password")).roles("USER").and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("password")).roles("USER","ADMIN");
}
}
9.2.2 基於數據庫表進行認證
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select user_id,user_pwd,true from t_user where user_id=?"
)
.authoritiesByUsernameQuery(
"select user_id,'ROLE_USER' from t_user where user_id=?"
)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
//.passwordEncoder(new BCryptPasswordEncoder());
}
}
注意 passwordEncoder( )這個方法,Spinrg實戰第4版給訊息:Spring Security的加密模塊包括了三個這樣的實現:BCryptPasswordEncoder、NoOpPasswordEncoder和StandardPasswordEncoder,也可以自己實現特定的加密解密算法。
不管使用那一個密碼轉碼器,都需要劣跡一點,數據庫中的密碼是永遠不會解碼的。所採取的的策略與之相反,用戶在登錄時輸入的密碼會按照相同的算法進行轉碼,然後再與數據庫中已經轉碼過的密碼進行對比。
簡單的說就是 如果你數據庫中存的是明文密碼,即假設123456,那是不需要這個屬性的,使用NoOpPasswordEncoder!但是這個“無”加碼器與後面的那個標準解碼器在新版本的Spring Security已經被棄用了,但是出於安全考慮的啓用,官方推薦不要使用明文存儲密碼 。
所以要麼使用啓用的No··Encoder 要麼在數據庫存儲的數據就採用算法進行加密
9.2.3 基於LDAP進行認證
9.2.4 配置自定義的服務
如果內置的用戶存儲無法通用認證需求時,纔有配置自定義的必要,假設用戶存儲在NoSQL中
public class SpitterUserService implements UserDetailsService {
@Autowired
private SpitterRepository spitterRepository; //注入
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
Spitter spitter=spitterRepository.findByUserId(userId); //查找Spitter
if(spitter!=null){
List<GrantedAuthority> authorities=new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER")); //創建權限列表
return new User( //返回User
spitter.getUserId(),
spitter.getUserPwd(),
authorities);
}
throw new UserIdNotFoundException();
}
}
}
@Autowired
SpitterRepository spitterRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throw Exception{
auth.userDetailsService(new SpitterUserService(spitterRepository));
}
通過實現UserDetailsService 可以不管用戶數據在哪,只會查找Spitter對象,甚至可以僞造一個,也不用關心底層所使用的數據存儲,只是獲得Spitter對象,並用它來創建User(User是UserDetails的具體實現)