사용자를 인증하는 가장 일반적인 방법 중 하나는 사용자 이름과 암호를 확인하는 것입니다. Spring Security는 사용자 이름 및 암호를 사용한 인증에 대한 지원을 합니다.
간단한 사용자 이름/비밀번호 예
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
}
사용자 정의 인증을 위한 Bean 공개AuthenticationManager
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(authenticationProvider);
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
위의 구성을 사용하면 @RestController
를 만들 수 있습니다.
@RestController
package com.example.hellosecurity.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
private final AuthenticationManager authenticationManager;
public LoginController (AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@PostMapping("/login")
public ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {
Authentication authenticationRequest =
UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());
Authentication authenticationResponse =
this.authenticationManager.authenticate(authenticationRequest);
// ...
return ResponseEntity.ok().build();
}
public record LoginRequest(String username, String password) {
}
}
일반적으로 Spring Security는 사용자 이름/암호 인증을 위해 내부적으로 구성된AuthenticationManager
를 빌드합니다. 사용하는 인스턴스를 사용자 정의하는 것이 여전히 필요할 수 있습니다. 예를 들어 캐시된 사용자에 대해 자격 증명 지우기를 사용하지 않도록 설정해야 할 수 있습니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
//...
}
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
ProviderManager providerManager = new ProviderManager(authenticationProvider);
providerManager.setEraseCredentialsAfterAuthentication(false);
return providerManager;
}
@Bean
public UserDetailsService userDetailsService() {
//...
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
또는 Spring Security의 전역을 빌드하는 데 다음과 같이 빌더를 구성할 수 있습니다.
전역 구성 AuthenticationManagerBuilder
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// ...
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
// Return a UserDetailsService that caches users
// ...
}
@Autowired
public void configure(AuthenticationManagerBuilder builder) {
builder.eraseCredentials(false);
}
}
DaoAuthenticationProvider는 UserDetailsService
및 PasswordEncoder
를 사용하여 사용자 이름과 암호를 인증하는 AuthenticationProvider 구현입니다.
로그인 검증 로직을 작성하기 위해 UserDetailsService를 구현해보겠다.
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity userData = userRepository.findByUsername(username);
if (userData != null) {
return new CustomUserDetails(userData);
}
return null;
}
}
DaoAuthenticationProvider 에서 UserDetails
를 조회합니다. UserDetailsService
UserDetailsService
에서 UserDetails
를 반환합니다. DaoAuthenticationProvider는 유효성을 검사한 다음 구성된 보안 주체가 있는 인증을 반환합니다.
UserDetails
인터페이스 구현
package com.example.testsecurity.config;
import com.example.testsecurity.entity.UserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class CustomUserDetails implements UserDetails {
private UserEntity userEntity;
public CustomUserDetails(UserEntity userEntity) {
this.userEntity = userEntity;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection <GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return userEntity.getRole();
}
});
return authorities;
}
@Override
public String getPassword() {
return userEntity.getPassword();
}
@Override
public String getUsername() {
return userEntity.getUsername();
}
}
로그인 요청이 들어오면 Security Config는 유효한 아이디인지 검사한다.
1. UserDetailsService 에서 user정보를 로드하여 UserDetails 객체를 뱉는다.
2. 생성된 userDetails 객체는 DaoAuthenticationProvider에 유효성을 검사한 다음 구성된 보안 주체가 있는 인증을 반환합니다