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