TIL - 0718

spring

[스프링빈 컨테이너와 DI]

  • 컨테이너에서 관리하고 있어야 컨테이너에 있는 다른 디펜던시를 인젝션 받을 수 있음 : 그 대상을 골라낸다음 해당 클래스를 읽기때문에(streotype 어노테이션이 설정되어있어야함)

[spring-mvc]

  • 실행시킬 때 import.sql을 사용하지않고, Repository에 엔티티 등록하기 : CommandLineRunner - 부트 실행 시 커맨드 실행
    • args로 Autowired처럼 가져올 수 있음
@SpringBootApplication
public class SecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }

    @Bean
    CommandLineRunner test(AccountRepository accountRepository, PasswordEncoder encoder) {
        return args -> {
            Account account = new Account("colin", encoder.encode("1234"), "colin", UserRole.USER, 1123213L, "http://www.naver.com");
            accountRepository.save(account);
        };
    }
}

/* 실행 로그 */
16:24:30.705 [TRACE] [restartedMain] [o.h.type.descriptor.sql.BasicBinder] - binding parameter [1] as [VARCHAR] - [colin]
16:24:30.705 [TRACE] [restartedMain] [o.h.type.descriptor.sql.BasicBinder] - binding parameter [2] as [VARCHAR] - [$2a$10$uFy4EBE4Bu.5SVBbpUYrc.ss6zNHWcvuWP6jBx41KVMfUMIFIGZ0y]
16:24:30.705 [TRACE] [restartedMain] [o.h.type.descriptor.sql.BasicBinder] - binding parameter [3] as [VARCHAR] - [http://www.naver.com]
16:24:30.707 [TRACE] [restartedMain] [o.h.type.descriptor.sql.BasicBinder] - binding parameter [5] as [BIGINT] - [1123213]
16:24:30.707 [TRACE] [restartedMain] [o.h.type.descriptor.sql.BasicBinder] - binding parameter [6] as [VARCHAR] - [colin]

[spring security]

form login flow

  1. 클라이언트가 인증 URI 요청 : /login
  2. 등록되어있는 필터(“/login”)에 요청이 걸림
  3. 필터는 프로바이더 매니져를 통해 프로바이더를 찾음 : /login 요청을 하면 필터는 우선 PreAuthenticationToken 객체를 생성함, PreAuthenticationToken을 처리하는 프로바이더를 찾는 것
  4. 프로바이더 처리 - PreAuthenticationToken에 저장되어있는 정보(request에서 파싱한 정보)와 저장되어있는 정보(Repository)를 비교한 후 PostAuthenticationToken을 생성하거나 예외(일치하는 아이디가 없거나 비밀번호가 일치하지않거나)를 발생시킴
  5. PostAuthentication이 정상적으로 생성되어 리턴되었을 때 필터의 성공 시 메소드로 감(SuccessHandler가 처리), 실패 시에 실패 메소드로 감(FailureHandler 가 처리)
    • 각각의 핸들러에서 HttpStatus와 헤더를 작성함 - response에 실어줌
    • 각각 인터페이스가 있고 구현체를 만들면 됨
    • 성공 시 jwt를 생성해서 response에 실어줌
  6. jwt 만료기간 전까지 클라이언트가 jwt를 실은 요청을 했을 때 서버는 DB 요청없이 jwt 시그니쳐 체크만 하면 됨

FormLoginFilter & FormLoginAuthenicationProvider

  • request에서 바디를 읽어(json - string) Dto로 변경(ObjectMapper)하고, PreAuthentication을 생성한 후 ProviderManager에 넘겨주면 적당한 Provider가 처리 후 PostAuthentication 생성
    • FormLoginAuthenticationProvider에 지원하는 Authentication 클래스로 PreAuthentication(지원 여부)을 걸어놨기때문에 매니져를 통해 찾음 : forEach로 찾음
    • Dto를 반드시 사용하는군
  • 필터와 프로바이더를 각각 등록해줘야함 : 프로바이더는 프로바이더 매니져에, 필터는 기본 필터(UsernamePasswordAuthenticationFilter) 전

jwt

  • DB 통신없이(상태 유지없이) 사용자 인증을 할 수 있게 해주는 인증방식 : 한번 인증하고, 토큰이 만료되기 전까지 인증 가능
  • claim set : 암호화되지않기때문에 보안에 유의해야하는 데이터를 넣으면 안됨, claim set에 들어가는 key-value가 많을수록 token의 길이가 길어짐

Filter & JWT Authentication Provider

  • HTTP 헤더 중 Authorization key의 값을 가지고와서 해당 권한을 가진 클라이언트의 요청인지를 판단하는 역할
    • 필터가 프로바이더에 연결시켜줌 : 프로바이더에 직접 접근하는게 아니라 프로바이더 매니져를 통해서 접근
  • Form/SocialLoginFilter는 최초 인증을 위한 필터고, JwtFilter는 인증을 한 클라이언트 중 권한 체크 과정으로 가기위한 필터

Success/Failure AuthenticationHandler

  • 성공, 실패 시 어떻게 처리를 할지에 대한 핸들링하는 역할 : 필터에서 호출을 함
    • 핸들러, jwt 팩토리 flow
  • ObjectMapper로 DTO(Objet)를 json으로 맵핑하거나 json을 DTO로 맵핑 할 때 자바빈 규약을 꼭 지켜야함 : 기본생성자 + setter/getter

JwtFactory

  • JWT를 생성하는 역할 : jwt0 library 사용
    • singing-key을 가지고 signature 생성 : 하드코딩 하면 안됨
    • String으로 리턴
    • RuntimeException이라서 따로 체크를 해줘야함 : 어디선가에서 처리를 해야하기때문에 예외 발생했을 때 어디선가는 잡아줘야하는데, non-check exception(runtime)이라 조심 뒤* SuccessHandler에서 JwtFactory를 사용해서 JWT를 생성함
    • 실패 시에는 다른 처리를 해야하기때문에 성공 시에만
    • 토큰 생성 후 Response(Body)를 통해서 전달 : json 타입으로 심어줌(Dto로 생성한 뒤) - ObjectMapper 사용(수동으로 Object -> json, json -> Object 맵핑하기위한 객체)