본문 바로가기
Spring|Spring-boot

OAuth 2.0 동작방식, 개발환경 구성, 클라이언트 등록, 프론트 개발

by oncerun 2023. 6. 21.
반응형

 

개발환경

  • OAuth 2.0
  • Naver, Google, Facebook OAuth2.0 연동
  • docker
  • Spring Security 3.1
  • Spring Data JPA
  • JDK 17
  • Spring boot 3.1
  • MySQL
  • OIDC
  • thymeleaf

 

테스트 개발환경

  • JUnit5
  • H2 DB

 

목표

1. JDK 17의 도입된 새로운 문법 및 API를 활용한다. 

2. OAuth 2.0의 개념 및 플로우를 학습한다.

3. Spring Security의 사용법이 변경됨으로 인해 이를 익히고 적용한다.

4. dockerFile을 개발환경 실습환경으로 구분해 작성하고 docker-compose를 통해 배포한다.

5. Spring boot 3.1 도입으로 인해 변경사항 및 릴리즈 내용을 학습한다. 

6. SSR의 타임리프를 통해 문법을 복습하고 프론트 개발을 진행한다.

7. MySQL을 사용하여 데이터를 저장하고, 민감한 사용자의 정보를 암호화하고 복호화하는 과정을 고민한다. 

 

Thymeleaf

우선 로그인 환경을 만들어야 한다. 

 

Pinterest는 OAuth 2.0을 통해 회원가입을 단순화 시킴으로써 사용자도 많이 늘고, 구글 및 페이스북 리소스를 잘 활용하여 유저의 이탈을 매우 잘 막은 케이스 중 하나이다. 

 

프론트는 핀터레스트의 UI를 가져온다. 

 

 

로그인 후에는 사용자의 정보를 보여주는 단순한 화면을 만들자. 현재 네이버, 페이스북, 구글에서 어떤 사용자 정보를 주는지 모르기 때문에 고정적인 화면을 넣어놓는다.

 

로그인 이후에는 해당 정보를 보여주도록한다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Main</title>
</head>
<body>
<h1 th:text="${user.email}"></h1>
</body>
</html>

 

 

 

Spring Security

https://spring.io/projects/spring-security

 

Spring Security

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications. Spring Security is a framework that focuses on providing both authentication and authoriz

spring.io

 

OAuth 2.0을 통해 로그인만을 진행하는데 왜 Spring Security가 사용될까? 

 

인증 이후 별다른 인가에 대한 로직이나 유저 플로우가 없는 경우는 굳이 사용하지 않아도 된다. 

특정 부분만 구현하여 보안적으로 튼튼하게 구현해주면 된다.

 

다만 로그인 이후, 유저에 대한 권한 설정을 통해 많은 서비스가 대부분 이루어지는데, 이 관리를 더 쉽게 하기 위함이다. 

예를 들어 관리자 혹은 일반 유저, 혹은 유저에 대한 등급제가 있는 경우 손쉽게 이를 관리할 수 있다. 

 

혹은 정말 개인정보를 잘 지킬 인프라가 구성되어 있어 별도의 개인정보를 관리하겠다면 인증, 인가, 보안에 대한 프레임워크 좋은 선택이라고 생각한다.

 

 

우선 Spring boot는 Spring Security 종속성이 있는 경우 다음과 같은 자동 설정을 진행한다.

또한 WebSecurityConfigureAdapter를 통해 설정하는 방법은 deprecated 되었기때문에 3.1에서는 사용할 수 없다.

 

@EnableWebSecurity
@Configuration
public class DefaultSecurityConfig {
    @Bean
    @ConditionalOnMissingBean(UserDetailsService.class)
    InMemoryUserDetailsManager inMemoryUserDetailsManager() {
        String generatedPassword = // ...;
        return new InMemoryUserDetailsManager(User.withUsername("user")
                .password(generatedPassword).roles("ROLE_USER").build());
    }

    @Bean
    @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
    DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(ApplicationEventPublisher delegate) {
        return new DefaultAuthenticationEventPublisher(delegate);
    }
}

 

user라는 이름과, 콘솔에 생성된 비밀번호를 통해 인증될 수 있도록 기본설정이 되어있다. 

 

Sprinbg Boot는 @Bean으로 퍼블리싱된 모든 필터를 애플리케이션 필터 체인에 추가한다고 한다. 

즉, 빈으로 등록된 모든 필터는 필터체인에 자동으로 등록된다고 한다.

 

우선 로그인 정책이 필요하다. 

 

1. main page는 반드시 로그인 이후 인증받은 사람만 접속할 수 있다.

2. 권한은 일반유저와 관리자로 나누어 진다. 

3. login page는 익명 사용자도 접근할 수 있도록 해야한다. 

4. 첫 접속 시 노출되어야할 page는 login page이다. 

 

 

현재 아키텍처는 다음과 같다.

 

 

Spring Security에서 FilterChainProxy를 통해 @Bean으로 등록된 SecurityFilterChain에게 인증,인가를 위임한다고 하는데, 필터체인프록시를 사용함으로써 얻는 이점은 다음과 같다고 설명한다. 

 

1. Spring Security의 서블릿을 보완하기 위한 첫 진입 포인트만을 제공한다.

만약 버그가 발생한다면  troubleshoot 위해서 우리는 단일 지점에 break point를 통해 동작 방식을 쉽게 디버깅할 수 있다.

 

클래스의 주석을 살펴보면 버전 3.1 부터 FilterChainProxy는 SecurityFilterChain 인스턴스 목록을 사용하여 구성되며, 각 인스턴스에는 RequestMathcer와 일치하는 요청에 적용하는 필터 목록이 포함되어 있다고 합니다.

 

SecurityFilterChain을 정의하여 이를 FilterChainProxy에 포함되도록 구성하면 될 것 같습니다.

 

2. 나머지 작업을 FilterChainProxy에서 처리해줄 수 있다.

메모리 누수 방지를 위해 SecurityContext를 지워준다. Firewall을 적용하여 특정 유형 공격으로 부터 보호하도록 구성할 수 있다. 

 

3. SecurityFilterChain 호출 시기를 보다 유연하게 결정할 수 있다.

 

이는 상당히 유용한 구조입니다. 

 

예를 들어 특정 URL 패턴에 대한 보안 필터링: FilterChainProxy는 RequestMatcher 인터페이스를 사용하여 HttpServletRequest의 속성(예: URL, 헤더, 파라미터 등)을 기반으로 필터 체인을 선택적으로 호출할 수 있습니다. 예를 들어, 특정 URL 패턴에 대해서만 인증 및 권한 검사 필터를 적용하고 싶을 때, FilterChainProxy는 해당 URL 패턴을 매칭하여 해당 필터 체인을 실행할 수 있습니다.

다중 보안 레벨 적용: 애플리케이션 내에 다중 보안 레벨이 필요한 경우가 있을 수 있습니다. 예를 들어, 일부 URL은 강력한 인증과 권한 검사를 요구하고, 다른 URL은 보다 느슨한 인증 수준만 필요로 할 수 있습니다. FilterChainProxy는 RequestMatcher를 사용하여 요청의 특성에 따라 다른 보안 필터 체인을 선택하여 적용할 수 있습니다.

동적 필터링: 일부 시나리오에서는 요청의 상태나 데이터에 따라 필터링이 달라져야 할 수 있습니다. 예를 들어, 사용자의 권한 또는 세션 정보에 따라 특정 필터를 적용하거나 제외해야 할 수 있습니다. FilterChainProxy는 RequestMatcher와 함께 사용하여 동적으로 필터 체인을 결정하고 필터링할 수 있습니다.

 

 

 

중간에 Adaptor를 하나 두어서 요청 URL과 일치하는지 여부를 찾고, 일치되는 SecurityFilterChain만을 실행시키는 다중 구조도 소개해준다. 

 

 

다음과 같이 목적에 맞게 코드를 작성하자. 

 

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorizeRequests -> authorizeRequests
                        .requestMatchers("/login", "/css/**", "/", "/img/**").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(AbstractHttpConfigurer::disable);

        return http.build();
    }

}

 

welcomePage에 대한 공식문서를 살펴보는 도중 welcomepage관련 내용을 보자.

 

Welcome Page

Spring Boot supports both static and templated welcome pages. It first looks for an index.html file in the configured static content locations. If one is not found, it then looks for an index template. If either is found, it is automatically used as the welcome page of the application.

 

첫 번째로 index.html 파일을 static 폴더에서 찾으며 발견되지 않은경우 index.template을 찾는다고 한다. 

 

그래서 index.html 파일을 main으로 변경하고, default 요청을 다음과 같이 매핑하였다.

 

@GetMapping("/")
public String index() {
    return "login";
}

 

OAuth 2.0 프로토콜에서는 다양한 클라이언트 환경을 위해 4가지 종류로 구분되어 제공된다고 한다.

 

Authorization Code Grant

 

권한 부여 승인 코드 방식으로, 기본 방식이라고 합니다. 

 

이 방식은 다음과 같이 동작합니다.

 

1. User(Resource Owner)는 Client 에게 Resource Server의 리소스 요청을 합니다. 

 

2. Client는 Authorization Server에게 권한 부여 승인 코드를 요청합니다. 

 

2-1. 이를 위해 Authorization Server에 미리 redirect_url 설정과 client key, secret key를 발급받아야 합니다.

 

2-2. 로그인 팝업이 출력되고 User는 로그인을 진행합니다.

 

2-3. 인증이 완료되면 기존에 설정된 redirect url로 권한 부여 코드를 url Parameter 방식으로 전달합니다.

 

이를 위해 다음과 같이 설정 및 코드를 작성하겠습니다.

 

1. google cloud console 검색 및 접근. 

2. 탐색메뉴 -> API 및 서비스 -> OAuth 동의 화면에서 각종 설정을 입력합니다. 

2-1. 만약 개발 환경이라면 도메인, 승인된 도메인은 생략합니다.

2-2. 만약 회사라면 설정 계정 및 지원 이메일을 받아야 할 것 같습니다.

 

 

3. 범위 설정을 하는데, 저는 google 에서는 openid도 요청하려고 합니다.

 

4. 사용자 인증 정보에서 redirection url을 입력합니다.

https 여야 정상동작 하는걸로 알고 있습니다만 업체 마다 다를 것 같습니다.

 

그럼 클라이언트 ID, PWD를 발급받았으니 이를 등록합니다.

 

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            clientId: google-client-id
            clientSecret: google-client-secret
# ...
  The default redirect URI template is {baseUrl}/login/oauth2/code/{registrationId}. The registrationId is a unique identifier for the ClientRegistration.

 

기본적으로 URI 템플릿을 제공해주며 registrationId는 google, naver와 같은 인증 서버를 말합니다.

 

위 url을 그대로 사용한다면 별도의 컨트롤러를 구현하지 않아도 된다고 합니다. 

 

 

여기서 잠깐 고민을 하게되었습니다.

 

 

사용자가 Google Login을 통해 Grant Code를 발급받는 것은 프론트엔드에서 해도 된다. 노출되는 정보인 클라이언트 id, scope, response_type 등은 노출되어도 크게 무리가 없다고 생각하고 a 태그를 통해 클릭과 동시에 로그인 화면으로 이동시키도록 했습니다. 

 

어찌저찌 요청하여 인증 코드와 클라이언트 secret key를 통해 사용자의 약간의 정보와 Access Token을 받았다고 하자. 

 

이를 그냥 우리 회원가입 데이터베이스에 넣고, 권한을 설정한 다음 이용하게 하면 끝인가? 

 

왜냐하면 Access Token을 사용하여 Resoruce Server에 어떠한 요청을 하지 않을 것이기 때문이다. 

 

그렇다면 로그인 프로세스는 다음과 같아진다고 생각한다..

 

1. 사용자가 구글로 로그인을 클릭한다.

 

2. 사용자가 리소스 서버로 부터 인증을 마무리 한다. 

 

3. redirect_url에 전달되어오는 grant code와 client secret key를 통해 사용자의 정보와 AccessToken 획득

 

4. 회원이라면 데이터베이스의 정보를 가지고 로그인 이후 프로세스로 넘어간다.

 

4-1 만약 회원이 아니라면 데이터베이스에 저장 한 다음 로그인 이후 프로세스로 넘어간다.

 

 

 

 

실제 응답값을 받지 않아서 JWT와 같이 사용자 정보도 같이 토큰화되어 전달되는지 모르겠다. 

JWT 전달해주면 이를 복호화 해야하는데.. 이 부분도 찾아봐야할 것 같다. 

 

위 로그인 프로세스를 Spring Security가 어느정도 지원해줄 것 같다.

 

예를들면 redirect_url부분에 대해서 인증과 실패를 필터링하여 인증시 다음 필터 체인으로 넘기고 실패시 인증 실패 프로세스로 넘어가는 과정에 대해 지원해주지 않을까 싶다.

 

grant code와 client secret key를 통해 내가 다시 Authorization Server에 인증하는 로직은 작성해야 할 것 같고 이에 대한 응답 값으로 security context에 보관할 것 같은 느낌이 든다.

 

혹은 데이터베이스 저장 로직도 작성해야 할 것 같다. 

 

우선 길이 길어졌기 때문에 실제 구현은 다음 글에서 이어서 작성하도록 하자. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[참고]https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html

[참고] https://blog.naver.com/mds_datasecurity/222182943542

 

 

 

 

반응형

'Spring|Spring-boot' 카테고리의 다른 글

to Kotlin  (0) 2023.07.06
OAuth 2.0 Spring Security 연동, 기능 파악  (0) 2023.06.27
Spring Boot Actuator  (0) 2023.02.16
Spring Boot 좀 더 자세히.  (2) 2023.02.04
Properties Bean PostProcessor  (0) 2023.02.02

댓글