1. 概述
本教程是 對 Spring Security 配置的介紹,它允許用户在不使用 XML 的情況下輕鬆配置 Spring Security。
Java 配置已於 Spring 3.1 中加入 Spring Framework,並在 Spring 3.2 中擴展,並通過標註帶有 @Configuration 註解的類來定義。
2. Maven 依賴項
為了將 Spring Security 集成到 Spring Boot 應用程序中,我們需要在我們的 pom.xml 中添加 spring-boot-starter-security 依賴項:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>3. 使用 Java 配置進行 Web 安全性
讓我們從一個基本的 Spring Security Java 配置示例開始:
@Configuration
public class SecurityConfig {
@Bean
public UserDetailsService inMemoryUserDetailsService(
PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}此配置類設置了一個基本基於 Spring Security 的內存中認證機制。
它定義了一個用户,用户名/密碼為 user/password。 此外,它分配了 USER 角色。 我們使用 InMemoryUserDetailsManager,該組件將用户詳細信息存儲在內存中。 此外,我們還需要一個 PasswordEncoder Bean,用於對用户的密碼進行編碼:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}4. Web 安全性
為了配置我們的授權規則和認證機制,我們發佈一個 SecurityFilterChain Bean:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.httpBasic(withDefaults());
return http.build();
}上述配置確保任何對應用程序的請求都使用 HTTP 基本身份驗證進行認證。
現在,當我們啓動應用程序並導航到 http://localhost:8080/app/ 時,會顯示一個基本身份驗證登錄提示。在輸入用户名“user”和密碼“password”後,服務器會驗證憑據。如果身份驗證成功,我們就可以訪問請求的資源。
5. 登錄表單
通過將 formLogin DSL 調用添加到我們的聲明中,我們可以添加基於瀏覽器的登錄功能和默認登錄頁面。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated())
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
}在啓動時,我們可以看到一個默認頁面已生成:
6. 基於角色的授權
現在,讓我們使用角色對每個 URL 配置簡單的授權:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/").hasRole("USER")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
}這些限制了對根目錄 (/) 的訪問,對於擁有 USER 角色的用户以及擁有 /admin/** 角色的用户,同時任何已認證的用户都可以訪問網站的其他部分。
請注意我們如何使用類型安全的 API 方法 hasRole。
7. 登出
與 Spring Security 的許多其他方面一樣,登出也提供了框架強大的默認設置。
默認情況下,登出請求會失效會話,清除所有身份驗證緩存,清除 SecurityContextHolder,並重定向到登錄頁面。
以下是一個簡單的登出配置:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.logout(withDefaults());
return http.build();
}然而,如果我們想要對可用的處理程序進行更精細的控制,以下是一個更完整的實現方案:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http,
LogoutSuccessHandler webSecurityUserLogoutHandler)
throws Exception {
http
// ...
.logout((logout) -> logout
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.logoutSuccessHandler(webSecurityUserLogoutHandler)
.deleteCookies("JSESSIONID")
);
return http.build();
}
@Bean
public LogoutSuccessHandler webSecurityUserLogoutHandler() {
return (request, response, authentication) -> {
System.out.println("User logged out successfully!");
response.sendRedirect("/app");
};
}此配置提供了一個乾淨且安全的註銷流程,同時在處理註銷後操作方面提供了靈活性。
8. 身份驗證
讓我們看看另一種使用 Spring Security 實現身份驗證的方法。
8.1. 內存身份驗證
請回想一下,在最初階段,我們使用了內存配置:
@Bean
public UserDetailsService inMemoryUserDetailsService(
PasswordEncoder passwordEncoder) {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder.encode("password"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}8.2. JDBC 身份驗證
為了將其遷移到 JDBC,首先,我們在 h2 依賴項添加到我們的 pom.xml 中:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>另外,我們需要在 application.properties 中配置 H2 數據庫:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.sql.init.schema-locations=classpath:org/springframework/security/core/userdetails/jdbc/users.ddl這定義了一個我們可以依賴的數據源,用於後續內容:
@Bean
public UserDetailsManager jdbcUserDetailsManager(DataSource dataSource,
PasswordEncoder passwordEncoder) {
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder.encode("password"))
.roles("ADMIN")
.build();
jdbcUserDetailsManager.createUser(user);
jdbcUserDetailsManager.createUser(admin);
return jdbcUserDetailsManager;
}此外,我們需要設置一個嵌入式的 DataSource,該 DataSource 初始化時使用默認的用户模式。
當然,通過以上兩個示例,我們也需要定義 PasswordEncoder Bean,如文檔所述。Spring Security 提供了一個在 org/springframework/security/core/userdetails/jdbc/users.ddl 中。
我們使用 @Profile(inmemory 或 jdbc)來選擇哪些 UserDetailsManager Bean 應該處於活動狀態:
spring.profiles.active=inmemory9. 結論
在本快速教程中,我們介紹了 Java 配置對於 Spring Security 的基礎知識,並重點展示了最簡單的配置場景的代碼示例。