23.10.03 항해 99 16기 주특기 프로젝트 15일차
오늘 공부한 것
* 페어가 작성한 미니프로젝트 정리 공부
1. Cors
1) Cors 란
아래 블로그는 내가 예전에 공부했던 것이고
https://nodaji1012-hanghae99-16.tistory.com/45
23.09.11~09.17 항해 99 16기 5주차 회고록
월요일에는 Lv3 과제 제출을 하고 JAVA 객체 지향을 다시 공부했다 화요일에는 Spring 심화 주차 강의를 들었고 Lv4 과제를 시작했다 수요일에는 Lv4 과제를 제출 했다 기존에 만들었던 것을 업그레이
nodaji1012-hanghae99-16.tistory.com
이 블로그는 페어분께서 참고하신 블로그다
https://ikadnorth.tistory.com/192
CORS, SOP
👣 개요 예를 들어, 피싱 사이트가 보통의 네이버 사이트(www.fake:80)를 모사해 피해자를 속였다고 가정해보자. 해당 사이트의 HTML에 포함된 JS 코드에 의해 현재 LocalStorage의 정보들을 모두 긁어서
ikadnorth.tistory.com
결론을 이야기하자면 Cors란 악의적인 개발자의 정보 남용을 막고자 웹 브라우저 차원에서 바련한 보안장치이다
서버가 허용한 웹 페이지에서만 해당 서버의 리소스를 확인 할 수 있다
2) Cors 테스트 방법
다양한 방법이 있지만 대표적인 방법인 preflight 방법을 이용해 테스트 코드를 작성했다
아래와 같이 OPTIONS 메서드로 Preflight 요청을 보내고 해당 응답에 HttpHeaders.ORIGIN 으로 표기했던 값이
정확히 돌아오는지를 확인하였다
@ActiveProfiles("prod")
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {
"MYSQL_DATABASE_USERNAME=test_account",
"MYSQL_DATABASE_PASSWORD=q1w2e3r4",
"MYSQL_URL=localhost:3306",
"OAUTH2_CLIENT_ID_KAKAO=1234567654123456",
"FRONTEND_ORIGIN=https://my-web-server-host",
"KEY_STORE_PASSWORD=q1w2e3r4"
})
class CorsTestInProfileProd {
@Autowired
private MockMvc mvc;
@WithMockUser //인증된 상태로 테스트를 진행하도록 도와줌
@Test
@DisplayName("[정상 작동] Preflight 요청 시, 해당 응답이 적절한지 확인")
void preflight() throws Exception {
// given
String clientOrigin = "https://my-web-server-host";
String method = "POST";
String host = "/api/signup";
// when & then
mvc.perform(
options(host)
.header(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, method)
.header(HttpHeaders.ORIGIN, clientOrigin)
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, clientOrigin));
}
}
2. 다양한 Profile
1) Profile 이란?
개발을 할때, 실제 서비스를 운영할때, 테스트를 할때 등등 설정값들은 각각 다를 수 있다
예를 들어 테스트를 할때는 QA를 위한 테스트 데이터를 DB에 사용할 수도 있고, MySQL이 아닌 H2DB를 사용할 수도
있다 반면, 실제 서비스를 운영할 때는 MySQL을 사용하고 배포한 MySQL 서버 URL 설정값을 사용해야 할 수도 있다
상황마다 application.properties 값을 변경해서 사용할 수도 있지만, Prod, Test, Local 상황마다 전환하는 것은
Human Error를 유발할 수 있고 비용이 많이 드는 작업이다
이를 위해 Spring Boot에서는 각 Profile에 따른 설정을 분리할 수 있도록 지원하고있다
2) 프로젝트에서의 Profile 운용 방식
- 총 3개의 Profiles를 사용했다
(1) Local
백엔드 개발을 위함
(2) Test
프론트엔드 팀의 편의를 위한 서버 실제로 EC2에 배포를 했다
다만, MySQL이 아닌 H2DB 사용
(3) Prod
실제 서비스를 위해 사용
3. SRP 원칙을 준수한 Spring Security 설정방법
1) SRP란?
SOLID 5원칙이라고도 불리는 OPP 5대 원칙중 Single Responsibilty Principle의 줄임말로
'1개의 클래스는 1개의 책임만 가져야한다' 라는 원칙
기존에 배운 Spring Security들은 '인가 경로정의', 'Cors 설정' 등이 모두 1개의 파일로면 해결되었다
하지만, 이것을 Profile을 이용하여 정의하려고 하니 코드 중복이 심하고 특정 기술을 위해 어떤 설정을 했는지 기록이
모호했기에 SRP 원칙을 지키는 파일로 변경하고자 Spring Injecting Collection 을 사용하기로 했다
2) Spring Injecting Collection
Bean Container는 Bean을 주입할때 Collection 으로도 DI가 가능하다
예를 들어 아래와 같은 주입이 가능하다 이렇게 받는 다면 같은 타입으로 선언된 Bean을 모두 호출할 수 있다
@Configuration
public Beans {
@Bean
public Something bean1() { ... }
@Bean
public Something bean2() { ... }
}
@Component
@RequiredArgsConstruction
public Client {
@Autowired
public Collection<Something> beans;
@Autowired
public List<Something> beans;
@Autowired
public Set<Something> beans;
@Autowired
public Map<String,Something> beans;
}
3) 실제 설정 사례
실제로는 FilterChainRing 이라는 인터페이스를 만들고
filterChainRingContainer라는 클래스를 선언하여
모든 Security 관련 설정들을 일괄 등록하는 코드를 만들었다
이로 인해 SRP 원칙뿐만 아니라 OCP원칙을 준수하는
Security 관련 설정들을 수행할 수 있게 되었다
4. DTO 직렬화, 역직렬화
1) Spring FrameWork에서의 직렬화, 역직렬화의 필요성
해당 서버로 들어오는 요청은 사실 텍스트로만 이루어진 글자들이다
Spring 에서는 이를 자바 객체로 사용할수 있도록 Form-data 혹은 Json 데이터를 Java Object로 역질렬화를 해주고
응답할 때는 반대로 Java Pbject를 직렬화하여 Json 혹은 Form-data로 변환하여 응답을 준다
때문에 실제로 직렬화, 역직렬화를 돕는 도구인 ObjectMapper에 대한 이해가 필요하다
2) ObjectMapper
Spring Framework에서 공식적으로 채택한 직렬화, 역직렬화를 위한 라이브러리의 대표적 도구로 Bean DI하여
사용할 수도있다
텍스트 원문을 Java Object로 변환해준다고는 하지만 모든 직렬화를 수행하지 않는다 조건은 아래와 같다
- 기본 생성자 + (public) Getter
- 기본 생성자 + (*) Setter
3) JsonCreator
ObjectMapper의 역직렬화를 위해 사용되는 어노테이션
만약 위와 같은 직렬화 조건을 만족할 수 없는 DTO라면
JsonCreator 어노테이션을 이용하여 역질렬화에 사용될
생성자를 지정할 수 있다
5. WithSecurityContext를 이용한 TestCode
1) WithSecurityContext 란?
Controller Layer에서 @AuthenticationPrincipal 을 사용할 때 UserDetails 객체를 해당 어노테이션이 적용된
파라미터에 주입한다
실제 UserDetails 값을 주입하는 것이기 때문에 @WithMocUser 를 이용해서 테스트를 진행하면 위와 같은
주입이 정상적으로 이뤄지지 않기 때문에 직접 SecurityContextholder 내부의 Principal을 작성해줘야 한다
물론 @WithMocUser 역시 SecurityContextholder 내부에 Principal을 주입하긴 하지만 User 라는 타입을
이용하여 주입하기 때문에 직접 구현한 UserDetailsImpl 을 넣어주는 것이 아니다
결론적으로 해당 Principal을 직접 넣기 위해 아래와 같은 코드를 작성했다