1. 개요
Spring Security 는 데이터베이스 및 UserDetailService 와 같은 다양한 인증 시스템을 제공합니다 .
JPA 지속성 계층 을 사용하는 대신 예를 들어 MongoDB 저장소 를 사용할 수도 있습니다 . 이 예제에서는 Spring Security와 MongoDB를 사용하여 사용자를 인증하는 방법을 볼 것입니다.
2. MongoDB를 사용한 스프링 Security 인증
JPA 리포지토리를 사용하는 것과 유사하게 MongoDB 리포지토리를 사용할 수 있습니다 . 그러나 이를 사용하려면 다른 구성을 설정해야 합니다.
2.1. 메이븐 의존성
이 예제에서는 Embedded MongoDB 를 사용할 것 입니다. 그러나 MongoDB 인스턴스와 Testcontainer 는 프로덕션 환경에 유효한 옵션일 수 있습니다. 먼저 spring-boot-starter-data-mongodb 및 de.flapdoodle.embed.mongo 의존성을 추가해 보겠습니다.
<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</artifactId>
<version>3.3.1</version>
</dependency>
2.2. 구성
의존성을 설정하고 나면 구성을 생성할 수 있습니다.
@Configuration
public class MongoConfig {
private static final String CONNECTION_STRING = "mongodb://%s:%d";
private static final String HOST = "localhost";
@Bean
public MongoTemplate mongoTemplate() throws Exception {
int randomPort = SocketUtils.findAvailableTcpPort();
ImmutableMongodConfig mongoDbConfig = MongodConfig.builder()
.version(Version.Main.PRODUCTION)
.net(new Net(HOST, randomPort, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
MongodExecutable mongodExecutable = starter.prepare(mongoDbConfig);
mongodExecutable.start();
return new MongoTemplate(MongoClients.create(String.format(CONNECTION_STRING, HOST, randomPort)), "mongo_auth");
}
}
또한 예를 들어 HTTP 기본 인증을 사용하여 AuthenticationManager 를 구성해야 합니다.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(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()
.disable()
.authorizeRequests()
.and()
.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
2.3. 사용자 도메인 및 저장소
먼저 인증 역할을 가진 간단한 사용자를 정의해 보겠습니다. Principal 개체 의 commons 메서드를 재사용하기 위해 UserDetails 인터페이스를 구현하도록 합니다.
@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 Test 로 마무리합시다 . 보시다시피 잘못된 자격 증명을 제공하거나 시스템에 존재하지 않는 사람에 대해 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 컨텍스트를 조롱하고 인증 및 권한 부여를 테스트하는 방법도 보았습니다.
항상 그렇듯이 이 예제의 코드는 GitHub에서 사용할 수 있습니다 .