1. 概述
Spring Security 提供多種身份驗證系統,例如通過數據庫和 UserDetailService。
與其使用 JPA 持久層,我們也可以使用,例如 MongoDB 倉庫。 在本教程中,我們將看到如何使用 Spring Security 和 MongoDB 認證用户。
2. 使用 Spring Security 與 MongoDB 進行身份驗證
類似於使用 JPA 倉庫,我們可以使用 MongoDB 倉庫。但是,為了使用它,我們需要配置不同的設置。
2.1. Maven 依賴
為了本教程,我們將使用嵌入式 MongoDB。 但是,MongoDB 實例和 Testcontainer 也可以作為生產環境的有效選項。首先,讓我們添加 spring-boot-starter-data-mongodb 和 de.flapdoodle.embed.mongo.spring30x 依賴項:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo.spring30x</artifactId>
<version>4.11.0</version>
</dependency>2.2. 配置
一旦我們設置了依賴項,就需要使用諸如 HTTP 基本身份驗證之類的配置來設置我們的 AuthenticationManager。
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {
//....
public SecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Bean
public AuthenticationManager customAuthenticationManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
return authenticationManagerBuilder.build();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.anyRequest().permitAll())
.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}2.3. 用户域和存儲庫
首先,讓我們定義一個具有角色的簡單用户,用於我們的身份驗證。它將實現 <em>UserDetails</em> 接口,以重用 <em>Principal</em> 對象中的常用方法。
@Document
public class User implements UserDetails {
private @MongoId ObjectId id;
private String username;
private String password;
private Set<UserRole> userRoles;
// getters and setters
}
現在我們已經有了用户,讓我們定義一個簡單的倉庫:
public interface UserRepository extends MongoRepository<User, String> {
@Query("{username:'?0'}")
User findUserByUsername(String username);
}2.4. 身份驗證服務
最後,我們來實現 UserDetailService 以檢索用户並檢查其是否已認證。
@Service
public class MongoAuthUserDetailService implements UserDetailsService {
// ...
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
com.baeldung.mongoauth.domain.User user = userRepository.findUserByUsername(userName);
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
user.getAuthorities()
.forEach(role -> grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole().getName())));
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}2.5. 測試身份驗證
為了測試我們的應用程序,讓我們定義一個簡單的控制器。例如,我們定義了兩個不同的角色,用於測試身份驗證和對特定端點的授權:
@RestController
public class ResourceController {
@RolesAllowed("ROLE_ADMIN")
@GetMapping("/admin")
public String admin() {
return "Hello Admin!";
}
@RolesAllowed({ "ROLE_ADMIN", "ROLE_USER" })
@GetMapping("/user")
public String user() {
return "Hello User!";
}
}讓我們用一個 Spring Boot 測試來驗證我們的身份驗證是否正常工作。正如你所看到的,我們期望當用户提供無效憑據或不在我們的系統中存在時,返回一個 401 狀態碼:
class MongoAuthApplicationTest {
// set up
@Test
void givenUserCredentials_whenInvokeUserAuthorizedEndPoint_thenReturn200() throws Exception {
mvc.perform(get("/user").with(httpBasic(USER_NAME, PASSWORD)))
.andExpect(status().isOk());
}
@Test
void givenUserNotExists_whenInvokeEndPoint_thenReturn401() throws Exception {
mvc.perform(get("/user").with(httpBasic("not_existing_user", "password")))
.andExpect(status().isUnauthorized());
}
@Test
void givenUserExistsAndWrongPassword_whenInvokeEndPoint_thenReturn401() throws Exception {
mvc.perform(get("/user").with(httpBasic(USER_NAME, "wrong_password")))
.andExpect(status().isUnauthorized());
}
@Test
void givenUserCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn403() throws Exception {
mvc.perform(get("/admin").with(httpBasic(USER_NAME, PASSWORD)))
.andExpect(status().isForbidden());
}
@Test
void givenAdminCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn200() throws Exception {
mvc.perform(get("/admin").with(httpBasic(ADMIN_NAME, PASSWORD)))
.andExpect(status().isOk());
mvc.perform(get("/user").with(httpBasic(ADMIN_NAME, PASSWORD)))
.andExpect(status().isOk());
}
}3. 結論
在本文中,我們探討了使用 Spring Security 進行 MongoDB 身份驗證。
我們已經瞭解瞭如何創建可工作的配置以及實現自定義 UserDetailService。我們還了解了如何模擬 MVC 上下文並測試身份驗證和授權。