1. UserDetailsService
역할:
UserDetailsService는 사용자의 **username(아이디)**를 기반으로 DB에서 사용자 정보를 조회하여, UserDetails 객체로 반환하는 인터페이스입니다.
주요 메서드:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
- 입력: 로그인 폼에서 입력된 사용자 이름
- 출력: 사용자 정보를 담은 UserDetails 객체
- 예외: 사용자를 찾지 못하면 UsernameNotFoundException 발생
사용 예시:
@Bean
public UserDetailsService customUserDetailsService() {
return username -> {
if ("user".equals(username)) {
return new User("user", "{noop}password",
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
}
throw new UsernameNotFoundException("User not found: " + username);
};
}
그리고 이렇게 Security 설정에 등록해 사용합니다:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin(withDefaults())
.userDetailsService(customUserDetailsService()); // 등록
return http.build();
}
2. UserDetails
역할:
UserDetails는 Spring Security가 이해할 수 있도록 사용자 정보를 담는 객체입니다. 이 객체를 통해 인증 처리와 권한 판단이 가능합니다.
주요 메서드:
메서드 설명
getUsername() | 사용자 이름 반환 |
getPassword() | 비밀번호 반환 |
getAuthorities() | 권한 리스트 반환 (예: ROLE_USER) |
isAccountNonExpired() | 계정 만료 여부 (true면 만료 안 됨) |
isAccountNonLocked() | 계정 잠금 여부 (true면 잠기지 않음) |
isCredentialsNonExpired() | 비밀번호 만료 여부 (true면 만료 안 됨) |
isEnabled() | 계정 활성화 여부 (true면 활성 상태) |
커스텀 구현 예시:
public class CustomUserDetails implements UserDetails {
private UserInfo userInfo;
public CustomUserDetails(UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userInfo.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
@Override public String getPassword() { return userInfo.getPassword(); }
@Override public String getUsername() { return userInfo.getUsername(); }
@Override public boolean isAccountNonExpired() { return true; }
@Override public boolean isAccountNonLocked() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isEnabled() { return userInfo.isEnabled(); }
}
동작 순서 정리
- 사용자가 로그인하면, Spring Security는 UserDetailsService.loadUserByUsername()를 호출합니다.
- DB에서 해당 사용자의 정보를 조회한 후, UserDetails 객체로 변환합니다.
- Spring Security는 이 객체를 이용해 인증(Authentication) 과정을 처리하고,
- 인증이 성공하면 Authentication 객체를 만들어 SecurityContext에 저장합니다.
Repository 연동 기반 예시
✅ 1. UserDetailsService 구현체
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
// 생성자 주입
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
return new CustomUserDetails(userInfo);
}
}
✅ 2. UserDetails 구현 클래스
public class CustomUserDetails implements UserDetails {
private final UserInfo userInfo;
public CustomUserDetails(UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userInfo.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
@Override
public String getPassword() {
return userInfo.getPassword();
}
@Override
public String getUsername() {
return userInfo.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return userInfo.isEnabled();
}
}
✅ 3. UserRepository (JPA)
@Repository
public interface UserRepository extends JpaRepository<UserInfo, Long> {
Optional<UserInfo> findByUsername(String username);
}
✅ 4. Security 설정 클래스
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
public SecurityConfig(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.userDetailsService(customUserDetailsService); // UserDetailsService 등록
return http.build();
}
}
✅ 5. UserInfo (Entity 예시)
@Entity
public class UserInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private boolean enabled;
@ElementCollection(fetch = FetchType.EAGER)
private List<String> roles = new ArrayList<>();
// getters, setters ...
}
'Spring-Security' 카테고리의 다른 글
커스텀 인증 필터에 명시적으로 DelegatingSecurityContextRepository, SecurityContext 추가 (0) | 2025.05.24 |
---|---|
SecurityContextHolderFilter & SecurityContextRepository (0) | 2025.05.24 |
AuthenticationManager 사용 방법 (0) | 2025.05.24 |
AuthenticationManager & AuthenticationManagerBuilder (0) | 2025.05.24 |
SecurityContext & SecurityContextHolder (0) | 2025.05.23 |