[혼자 구현하는 웹 서비스] 5장 스프링 시큐리티 & OAuth 2.0

2020. 7. 17. 16:39책/스프링 부트와 AWS로 혼자 구현하는 웹 서비스

5장 스프링 시큐리티와 OAuth으로 로그인 기능 구현하기

스프링 시큐리티와 OAuth 모두 인프런 또는 생활코딩 강의를 통해 한 번씩 접해본 기술이다.

하지만 실제 서비스에 사용되는 것처럼 코드를 작성해 본 적이 없었기 때문에, 흥미롭게 진행하였다.

 

1.  스프링 시큐리티와 OAuth

스프링 시큐리티는 막강한 인증과 권한부여 기능을 가진 프레임 워크이다. 사실상 스프링 기반의 애플리케이션에서는 보안을 위한 표준이라고 보면 된다. 수많은 서비스에서 소셜 로그인을 사용하는 이유는 해당 과정을 직접 구현할 경우 굉장히 많은 내용을 구현해야 하기 때문이다. 대표적으로 로그인 시 보안, 비밀번호 찾기, 비밀번호 변경, 회원가입 시 인증절차 등이 있다. OAuth를 이용한 소셜 로그인 기능을 사용하면 해당 기능들을 구글, 페이스북, 네이버 등에서 해결하니 좀 더 서비스 개발에 집중할 수 있게 된다.

 

2. 사용자 정보 클래스

구글 로그인 기능을 추가하기 위한 수많은 자료가 있다. 그중 가장 먼저 해야할 일은 구글 클라우드 플랫폼을 통해 구글 서비스를 등록하는 과정이다. 해당 과정은 단순히 따라 하는 과정이기 때문에 스크린숏은 생략하고 진행하였다.

 

해당 과정중 client-id와 client-secret값이 명시된 properties를. gitignore에 추가하였지만 동작하지 않았다. 이것이 git의 캐시 문제라고 하는데, 해결하는 과정이 꽤 간단했다. 책에 내가 겪을 에러에 대해 미리 명시되어 있어서 참 편리하고 좋았다. 

구글의 로그인 인증정보를 발급 받았다면, 프로젝트 구현에 사용하면 된다.

먼저, 사용자 정보를 담당할 도메인을 생성한다.

User entity는 이러한 상태를 가지도록 구성한다. 이때 @Enumrated(EnumType.STRING)은 Enum 값을 어떤 형태로 저장할지를 결정해주는 어노테이션이다. 기본적으로 int로 저장되지만, 위와 같이 명시해서 타입을 변경할 수 있다. 

사용자의 권한을 관리할  Enum클래스 Role은 위와같이 구성한다. 

스프링 시큐리티에서는 권한 코드에 항상 ROLE_ 을 앞에 붙여야 한다. 

Entity User의 CRUD를 책임질 UserRepository를 생성한 후, 시큐리티 설정을 진행한다.

 

3. 스프링 시큐리티 설정

먼저, 스프링 시큐리티 관련 의존성을 추가해준다. 

위 의존성은 소셜 로그인 등의 클라이언트 측에서 소셜 기능 구현시에 필요한 의존성이다. 기본적으로 spring-security-oauth2-client와 spring-security-oauth2-jose를 관리해준다고 한다.

 

의존성을 추가했다면, 시큐리티 관련 클래스들을 한곳에 모을 패키지를 만들고, 필요한 클래스들을 작성한다.(내용이 방대하므로 코드는 따로 적지 않겠다. 코드를 작성하며 공부한 내용이 많은데 코드와 함께 보아야 이해가 되므로... 해당 내용들은 스프링 시큐리티에 관한 내용을 정리할 때 따로 정리해야겠다.)

 

 

4. 기존 테스트에 시큐리티 적용

시큐리티와 OAuth를 이용하여 소셜 로그인 기능을 구현한 후, gradle > tasks > verification > test를 통해 전체 테스트 코드를 모두 실행시켜 보면 롬북 외의 테스트 코드가 모두 실패하는 것을 볼 수 있다. 책에서는 크게 3가지 문제로 나누어 해결책을 제시해 주었다. 

 

1. CustomOAuth2 UserService를 찾을 수 없다.

>> 이것은 소셜 로그인과 관련된 설정값들이 없어서 실패하는 경우이다. application-oauth.properties에 설정값들을 추가해주었지만 이것은 src/main 환경에만 적용이 된다. 기본적으로 스프링 부트는 src/main과 src/test가 각각 본인만의 환경 구성을 가진다. src/main/resources/application.properties의 내용이 test에서도 적용될 수 있었던 이유는 test에 application.properties가 없으면 main의 설정을 그대로 가져오기 때문이다. 다만 이렇게 자동으로 main의 설정을 적용하는 범위는 application.properties까지이다. 소셜 로그인을 위한 설정값들을 따로 모아둔 properties는 적용되지 않는다. 이것을 해결하기 위해 test단에도 application.properties를 만들어서 임시 값을 넣어준다.

 

2. 302 Status Code

두 번째로는 post등록 테스트에서 응답의 결과로 200 status code가 아닌 302 status code가 와서 발생하는 오류이다. 302는 리다이렉션 응답을 의미하는데, 이는 스프링 시큐리티 설정 때문에 인증되지 않은 사용자의 요청은 이동시키기 때문이다. 이러한 경우 스프링 시큐리티에서 공식적으로 방법을 지원하고 있으므로 spring-security-test 의존성을 추가하고 해결할 수 있다. 

 

3. @WebMMvcTest에서 CustomOAuth2 UserService를 찾을 수 없음

이것은 @WebMvcTest가 CustomOAuth2 UserService를 스캔하지 않기 때문에 발생하는 문제이다. @WebMvcTest는 @Controller를 비롯한 @ControllerAdvice, WebMvcConfigurer등을 읽지만 @Repository, @Service, @Component는 스캔하지 않는다. 여기서는 SecurityConfig를 읽지만 이것과 관련된 @Service인 CustomOAuth2UserService를 읽지 못해 생기는 문제이다. 따라서 ServiceConfig를 읽지 않도록 설정해주면 문제를 해결할 수 있다.

 

 

+ 도중에 config파일의 위치를 잘못 설정하여 다른 패키지로 이동을 시켰는데, bean이 중복된다는 에러가 발생하였다. 파일을 복사한 것이 아닌 이동한 것이었기 때문에 중복되었다는 빈을 찾지 못하였다. 이를 해결하기 위해서는./gradlew clean을 통해 caching 되어 있는 빈을 삭제해주는 수밖에 없었다... 많은 삽질 끝에 얻어낸 지식이라 나중에 또 써먹을 일이 있을 것 같아서 적어둔다!