Spring Security
: 인증(Authentication)과 권한(Authorization)을 처리하기 위한 강력한 프레임워크
스프링 시큐리티를 구현에 필요한 SimpleGrantedAuthority, UserDetails, UserDetailsService 에 대해 중점적으로 정리
SimpleGrantedAuthority
- 사용자가 가진 권한(Role)을 문자열 형태로 표현하는 클래스
public final class SimpleGrantedAuthority implements GrantedAuthority
- GrantedAuthority 인터페이스의 구현체
- 문자열 기반의 권한( ex: "ROLE_USER", "ROLE_ADMIN")을 저장
- 권한 체크 시 비교 기준이 됨
- 주로 UserDetails.getAuthorities() 안에서 사용
# 권한(Role) 비교시 참고사항
"ROLE_" prix는 관례이자 규칙!
-> hasRole("ADMIN")은 내부적으로 hasAutority("ROLE_ADMIN")로 해석
ex ) SecurityConfig 에서 사용
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/mypage").hasRole("USER")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().permitAll()
)
// ... 생략 ...
return http.build();
}
- Spring Security의 authorizeHttpRequests() 안에서 요청 경로별로 권한을 검사할 때 사용하는 메서드
- "/mypage" 경로는 ROLE_USER 권한을 가진 사용자만 접근 가능
- hasRole("USER")라고 쓰지만, **Spring Security는 내부적으로 "ROLE_" + role**로 처리함
-> 즉, 실제로는 SimpleGrantedAuthority("ROLE_USER")가 있는지를 검사함
# 정리
| 메서드 | 의미 | 내부 처리 |
| hasRole("USER") | "ROLE_USER" 권한이 있는가? | hasAuthority("ROLE_USER")로 변환 |
| hasAuthority("ROLE_USER") | 정확히 "ROLE_USER" 권한이 있는가? | 그대로 사용 |
UserDetails
- 인증된 사용 정보를 담는 객체
- 스프링 시큐리티가 인증 후 사용자 정보를 담기 위해 사용하는 인터페이스
- 직접 클래스를 만들고 이 인터페이스를 구현해야함
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
default boolean isAccountNonExpired() {
return true;
}
default boolean isAccountNonLocked() {
return true;
}
default boolean isCredentialsNonExpired() {
return true;
}
default boolean isEnabled() {
return true;
}
}
# 메서드 설명 기본값
| 메서드 | 설명 | 기본값 |
| getAuthorities() | - SimpleGrantedAuthority("ROLE_USER") 같은 권한 객체들을 담은 리스트 - 로그인 후 권한 체크에 사용됨 (hasRole, hasAuthority 등) |
- |
| getPassword() | 암호화된 비밀번호( 이터베이스에 저장된 암호화된 비밀번호) | - |
| getUsername() | - 로그인 ID (보통 이메일) | - |
| isAccountNonExpired() | - 계정 만료 여부 - 만료되었으면 로그인 불가. (ex. 유효 기간 지난 계정) - 기본값은 만료 안 됨 (true) |
true |
| isAccountNonLocked() | - 계정 잠금 여부 - 보통 로그인 실패 횟수 초과 시 잠금 처리할 때 사용 |
true |
| isCredentialsNonExpired() | - 비밀번호 만료 여부 - 보안 정책상 주기적으로 비밀번호를 바꾸게 할 때 활용 가능 |
true |
| isEnabled() | - 계정 활성 여부 - 관리자가 비활성 처리한 경우 false로 설정할 수 있음 |
true |
# UserDetail 인터페이스를 구현한 예시
public class PrincipalUserDetails implements UserDetails {
@Getter
private final Member member; // 직접 만든 Member 엔티티
public PrincipalUserDetails(Member member) {
this.member = member;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(member.getRole()));
}
@Override
public String getPassword(){
return member.getPassword();
}
@Ovrride
public String getUsername(){
return member.getEmail();
}
// 계정 만료, 잠김 여부 등 설정
@Ovrride
public boolean isAccountNonExpired() { return true; }
@Ovrride
public boolean isAccountNonLocked() { return true; }
@Ovrride
public boolean isCredentialsNonExpired() { return ture; }
@Ovrride
public boolean isEnabled() { return true; }
}
UserDetailService (인터페이스)
- 사용자의 이름(username, email 등)을 이용해 DB 에서 사용자 정보를 조회하고, UserDetails 객체로 반환하는 역할
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
# UserDetails 를 반환하는 서비스 ( 실제 사용 )
@Service
public class PrincipalDetailsService implements UserDtailsService {
private final MemberRepositorty memberRepository;
public PrincipalDetailsService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Ovrride
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
MemberVO member = accountRepository.findUserByEmail(email); // email로 사용자 조회
if (member == null) throw new UsernameNotFoundException("존재하지 않는 사용자");
return new PrincipalUserDetails(member);
}
}
- 클라이언트가 로그인 시 email/password 를 입력하고 요청하면, Spring Security 에서 내부적으로 UserDetailService.loadUserByUsername() 을 호출하여 해당 메서드에서 DB 로 부터 유저 정보를 조회해옴
# 로그인 과정 요약
[사용자 로그인 요청(email, password)]
↓
[Spring Security 필터]
↓
[UserDetailsService.loadUserByUsername(email)]
↓
[DB에서 사용자 조회 → UserDetails 반환]
↓
[Spring Security가 비밀번호 일치 확인]
↓
[인증 성공 or 실패]
'Backend > Spring & Spring Boot' 카테고리의 다른 글
| 스프링 롬복 인식 실패 문제 해결 (4) | 2025.06.16 |
|---|---|
| @RequiredArgsConstructor 의 역할 ( 생성자 주입 vs 필드 주입 ) (1) | 2025.06.05 |
| Spring Security 의 초기 username 설정 위치 (0) | 2025.04.29 |
| AOP 동작 방식 (0) | 2025.02.04 |
| Spring에서 @Autowired와 static 메서드 사용 시 NullPointException (0) | 2025.01.22 |