全新Spring Security安全管理配置使用詳解
環境:SpringBoot2.7.12 + JDK21
1. 簡介
Spring Security 是一個提供身份驗證、授權和防護常見攻擊的框架。它為確保命令式和反應式應用程序的安全提供一流的支持,是確保基于 Spring 的應用程序安全的事實標準。
Spring Scurity核心分為2大模塊:
- 認證(Authentication):認證是建立一個他聲明的主體的過程(一個主體一般是指用戶、設備或一些可以在你的應用程序中執行的其他系統)。常見的身份認證一般要求用戶提供用戶名和密碼。系統通過校驗用戶名和密碼來完成認證過程。
- 授權(Authorization):當身份認證通過后,去訪問系統的資源,系統會判斷用戶是否擁有訪問該資源的權限,只允許訪問有權限的系統資源,沒有權限的資源將無法訪問,這個過程叫用戶授權。比如會員管理模塊有增刪改查功能,有的用戶只能進行查詢,而有的用戶可以進行修改、刪除。一般來說,系統會為不同的用戶分配不同的角色,而每個角色則對應一系列的權限。
從Spring Security5.7開始之前的安全配置方式及Web授權處理方式發生了變化,接下來將詳細介紹配置的變化。
2. 安全配置
2.1 配置方式
在5.7之前的版本自定義安全配置通過如下方式:
@Component
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// ...
}
}
在5.7之后推薦如下配置方式
@Configuration
public class SecurityConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain controllerFilterChain(HttpSecurity http) throws Exception {
// ...
return http.build() ;
}
}
通過配置安全過濾器鏈的方式配置,具體內部的配置細節還都是圍繞著HttpSecurity進行配置。
2.2 配置不同的過濾器鏈
在一個配置文件中我們可以非常方便的配置多個過濾器鏈,針對不同的請求進行攔截。
@Configuration
public class SecurityConfig {
@Bean
@Order(1)
SecurityFilterChain controllerFilterChain(HttpSecurity http) {
// 當前過濾器鏈只對/demos/**, /login, /logout進行攔截
http.requestMatchers(matchers -> matchers.antMatchers("/demos/**", "/login", "/logout")) ;
http.authorizeHttpRequests().antMatchers("/**").authenticated() ;
http.formLogin(Customizer.withDefaults()) ;
// ...
return http.build() ;
}
@Bean
@Order(2)
SecurityFilterChain managementSecurityFilterChain(HttpSecurity http) throws Exception {
// 該過濾器只會對/ac/**的請求進行攔截
http.requestMatchers(matchers -> matchers.antMatchers("/ac/**")) ;
// ...
http.formLogin(Customizer.withDefaults());
return http.build();
}
}
以上配置了2個過濾器鏈,根據配置的@Order值,優先級分別:controllerFilterChain,managementSecurityFilterChain。當訪問除上面指定的uri模式以為的請求都將自動放行。
2.3 用戶驗證配置
在5.7版本之前,我們通過如下配置配置內存用戶
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("admin")
.password("123123")
.authorities(Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"))) ;
}
}
5.7 只有由于推薦的是通過自定義SecurityFilterChain的方式,所以我們需要通過如下的方式進行配置:
@Configuration
public class SecurityConfig {
@Bean
@Order(0)
SecurityFilterChain controllerFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder builder = http.getSharedObject(AuthenticationManagerBuilder.class) ;
builder.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("admin")
.password("123123")
.authorities(Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"))) ;
// ...
}
}
2.4 授權方式
在5.7之后推薦配置認證授權的方式如下
@Bean
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().antMatchers("/users/**").hasAnyRole("ADMIN") ;
// ...
return http.build() ;
}
通過上面的authorizeHttpRequests方式進行授權配置,會向過濾器鏈中添加AuthorizationFilter過濾器。在該過濾器中會進行權限的驗證。
2.5 自定義授權決策
如果需要對請求的uri進行更加精確的匹配驗證,如:/users/{id},需要驗證只有這里的id值為666才方向。
@Bean
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(registry -> {
registry.antMatchers("/users/{id}").access(new AuthorizationManager<RequestAuthorizationContext>() {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
RequestAuthorizationContext object)
// 獲取路徑上的值信息,其中key=id,value=xxx
Map<String, String> variables
// 這里的第一個參數是boolean,確定了授權是否通過
return new AuthorityAuthorizationDecision(variables.get("id").equals("666"), Arrays.asList(new SimpleGrantedAuthority("D"))) ;
}
}) ;
}) ;
}
2.6 全局認證
如果配置了多個不同的SecurityFilterChain,而每個認證都使用相同的用戶體系,那么我們可以定義AuthenticationProvider或者UserDetailsService 類型的Bean即可。
@Bean
UserDetailsService userDetailsService() {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
} ;
}
@Bean
AuthenticationProvider authenticationProvider() {
return new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return false;
}
} ;
}
以上是本篇文章的全部內容, 希望對你有所幫助。
完畢!!!