공부기록/Spring Security

[이론] 스프링 시큐리티1

jhs0129 2022. 12. 18. 19:29
320x100
반응형

목차

스프링 시큐리티란?

스프링 기반의 애플리케이션 보안(인증, 권한, 인가)를 담당하는 스프링 하위 프레임워크

  • 인증: 접근하는 사용자가 누구인지 확인(현관문 벨을 눌렀을 때 '누구세요?'라고 묻기)
  • 인가: 인증된 사용자에 대해서 권한을 확인하고 허락(누구인지 듣고 알고 있는 사람이라면 문 열어주기)

스프링 시큐리티 기본 동작 흐름도

기본적으로 인터페이스를 상속받아 기능을 구현을 한다

우선 지금은 UserDetailsService(사용자 세부 정보 서비스), PasswordEncoder에 대해서 알아본다


UserDetailsService

사용자 관리

  • UserDetails
    • 스프링 시큐리티에서 사용자를 정의하는 모델
    • 스프링 시큐리티가 이해하는 방식으로 사용자를 나타내기 위한 모델
    • 하나 이상의 권한(GrantedAuthority)을 가진다
  • UserDetailsService
    • 사용자 이름으로 검색하는 역할
    • 인증을 완료하는데 있어서 반드시 필요한 유일한 작업
    • 재정의 후 Bean 등록
  • UserDetailsManager
    • UserDetailsService 상속받아 추가적인 작업 수행 가능
    • 사용자 추가, 수정, 삭제 작업

사용자를 인증하는 기능만 필요한 경우 UserDetailsService만 구현하면 필요한 기능을 제공할 수 있다

UserDetails

기능 목록

  • getUsername()
  • getPassword()
  • getAuthorities()
  • isAccountNonExpired()
  • isAccountNonLocked()
  • isCredentialsNonExpired()
  • isEnabled()

데이터베이스 사용시 UserDetails 정의 방법

public class DbUser implements UserDetails {
    private final User user;

    public DbUser(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(() -> user.getAuthority());
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

UserDetailsService

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

딱히 추가적인 기능은 없고 해당 사용자가 존재하는지 여부만 판별한다

UserRepository를 이용하여 사용자 존재 여부의 로직을 구현하면 된다


PasswordEncoder

암호 검증, 암호화 역할을 함

public interface PasswordEncoder {
  String encode(CharSequence rawPassword); // 암호화
  boolean matches(CharSequence rawPassword, String encodedPassword); // 검증
  default boolean upgradeEncoding(String encodedPassword) { // 보안 향상을 위해 재 인코딩
      return false;
  }
}

제공되는 옵션

  • NoOpPasswordEncoder: 암호화 하지 않음
  • StandardPasswordEncoder: SHA-256 사용
  • Pbkdf2PasswordEncoder: PBKDF2 사용
  • BCryptPasswordEncoder: bcrypt 해싱 함수 사용
  • SCryptPasswordEncoder: scrypt 해싱 함수 사용

여러 Encoder 사용시 설정 방법

DelegatingPasswordEncoder 사용

Map의 key 값을 접두사로 암호앞에 붙여서 사용

ex) {bcrypt}12345

@Bean
public PasswordEncoder passwordEncoder(){
    Map<String, PasswordEncoder> encoders = new HashMap<>();

    encoders.put("noop", NoOpPasswordEncoder.getInstance());
    encoders.put("bcrypt", new BCryptPasswordEncoder());
    encoders.put("scrypt", new SCryptPasswordEncoder());

    return new DelegatingPasswordEncoder("bcrypt", encoders);
//    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

PasswordEncoder나 UserDetailsService를 직접 등록을 하나만 한 경우 오류가 나게 된다 필히 두개를 같이 등록해야 한다

320x100
반응형