Programming-[Backend]/Keycloak

Keycloak - 6. 애플리케이션과 통합, 리버스 프록시

컴퓨터 탐험가 찰리 2024. 8. 2. 08:46
728x90
반응형

KeyCIoak - 모던 애플리케이션을 위한 ID 및 접근관리 | 에이콘출판(주) | 스티안 토르거센, 페드로 이고르 실바 지음, 최만균 옮김

서적을 참고하여 요약한 시리즈 글이다.

 


 

1. 애플리케이션 실행

 

이번에는 교재에서 제공하는 코드 중 ch7의 코드를 애플리케이션의 하나로 실행할 것이다. 그리고 이 애플리케이션의 포트가 8080이기 때문에, 기존에 실행하던 keycloak용 docker container는 잠시 멈추고 8180번으로 keycloak container를 다시 실행한다. 그냥 애플리케이션의 포트를 8080이 아닌 다른 포트로 하고 싶기도 한데, 그럼 교재와 다르게 또 바꿔야할 부분들이 많을 것 같아서 그냥 교재에 나온대로 해본다.

 

1.1 Keycloak 세팅

8080에 떠있는 container를 종료하고, 다음 명령어를 실행한다.

$ docker run -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 keycloak/keycloak start-dev

 

그리고 keycloak admin 클라이언트에 접속하여 신규 realm을 생성한다. 이름은 이전처럼 myrealm으로 생성한다.

 

그리고 브라우저 기반 앱을 보호하기 위한 mybrowserapp 클라이언트를 생성한다. 반드시 myrealm에 만들어야한다!

  • Client ID: mybrowserapp
  • Root URL: http://localhost:8080

서버 사이드 웹 애플리케이션을 보호하기 위한 mywebapp 클라이언트를 사용한다.

  • Client ID: mywebapp
  • Root URL: http://localhost:8080
  • Access Type: Confidential

백엔드 애플리케이션을 보호할 클라이언트를 생성한다.

  • Client ID: mybackend
  • Root URL: http://localhost:8080
  • Access Type: Confidential
  • Direct Access Grant Enabled: ON

Direct Access Grant Enabled 옵션은 보통 OFF로 처리한다. 이번 예제에서 브라우저를 사용하지 않고 Access Token을 획득하는 과정을 보여주기 위해서 ON 처리한다.

 

애플리케이션 앞단에서 실행되는 리버시 프록시용 클라이언트도 생성한다.

  • Client ID: proxy-client
  • Root URL: http://localhost
  • Access Type: Confidential

사용자를 생성한다.

  • username: alice
  • password: alice

 

2. 통합 아키텍처 및 통합 옵션

 

2.1 통합 아키텍처

2.1.1 Embedded

기술 스택을 통일하여 통합하는 형태다. 애플리케이션이 직접 Keycloak과 통신하고 처리하는 방식이다.

 

2.1.2 proxy

프록시 스타일은 애플리케이션 앞단에 proxy를 두고, 토큰을 가져오거나 보안 관련 데이터를 가져오기 위해 HTTP 헤더에 의존한다.

 

Embedded 스타일은 하나로 통합되기 때문에 좀 더 간결하다는 장점이 있다. 프록시 스타일은 애플리케이션이 레거시 라던가 하는 사유로 제어하기 어려운 경우 추천되는 스타일이다. 정답은 없다.

 

2.2 통합 옵션

어떤 클라이언트 사이드 도구를 선택할 지에 관한 것이다. 폭넓게 사용되고, 활발한 유지보수가 되고, OAuth2 및 OpenID connect 명세를 잘 지키는 옵션을 사용하면 된다. 

 

 

3. 애플리케이션 통합

 

책에서 제공하는 통합용 언어 목록은 다음과 같다. 이 모든 언어들에 대해서 클라이언트, 리소스 서버 생성법을 논의한다.

  • golang
  • Quarkus
  • Java
  • Spring Boot
  • Javascript
  • Node.js
  • python

이 중 내가 가장 친숙하고 실무에서 사용하게될 SpringBoot에 대해서만 일단 정리해놓는다.

 

 

3.1 SpringBoot 애플리케이션 통합 - 프로젝트 실행

ch7/springboot/frontend의 코드를 참고한다.

 

OAuth2/OpenID Connect 지원 기능을 활성화하기 위해서 pom.xml에 추가되어있는 두 의존성을 확인한다.

  • spring-boot-starter-oauth2-client
  • spring-boot-starter-security

 

그리고 application.yml에 들어가서 client-secret 값을 mywebapp용 secret 값으로 변경해준다. provider.keycloak: 하위 url들에서는 '/auth' 경로를 제거한 url을 넣었다. 필요하다면 /auth를 다시 넣어주면 될 것 같다.

 

다음으로 애플리케이션을 실행한다. maven wrapper를 이용하여 실행하면 된다.

$ cd Keycloak-Identity-and-Access-Management -for-Modern- Applications/ch7/springboot/frontend
$ ./mvnw spring-boot: run

 

나의 경우 잘 실행이 되지 않아서, spring boot, java 및 maven plugin을 업데이트하고 다시 build 한 뒤에 진행했다.

pom.xml에서 다음 사항들을 수정한다.

 

Save를 한 뒤, 아래 명령어들을 입력한다.

 

$ ./mvnw clean -U install
$ ./mvnw spring-boot:run

 

 

그 후에 localhost:8080 으로 접속하면 로그인 화면이 뜨고, 로그인을 완료하면 아래처럼 문구가 출력되는 것을 볼 수 있다.

 

 

이런 과정을 통해 프론트엔드로 사용자가 인증된 사용자가 되는 과정을 테스트해봤다.

 

 

3.2 리소스 서버 생성

ch7/backend 디렉토리로 이동한다. 의존성은 프론트엔드와 비슷하다. 아래 의존성들을 pom.xml에서 확인한다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

 

 

이 리소스 서버는 JWT 토큰의 유효성을 검사하는 역할을 한다. 여기서도 application.yml 파일의 '/auth' 경로를 제거해주었다.

 

켜져있는 프론트엔드 서버를 종료하고, 아래처럼 명령어를 통해 실행한다.

$ ./mvnw clean -U install
$ ./mvnw spring-boot:run

 

리소스 서버이므로 아래 코드처럼 curl 요청을 보내서 토큰을 얻어오고 저장한다.

 

응답 JSON에서 access_token 값을 얻어오기 위해 JSON 파싱 CLI 툴인 jq를 설치한다.

$ brew install ja

 

CLIENT_SECRET 값을 mybackend의 SECRET 값으로 변경하고, 중간중간 있는 개행문자인 (\)가 끝에 오도록 잘 처리해서 터미널에서 실행해야한다. 또한 mybackend: 뒤에 공백 없이 SECRET 값이 바로 붙어서 와야한다.

$ curl -X POST http://localhost:8180/realms/myrealm/protocol/openid-connect/token \
--user mybackend:{CLIENT_SECRET} -H 'content-type: application/x-www-form-urlencoded' \
-d 'username=alice&password=alice&grant_type=password' | jq -r ".access_token"

 

 

아래 처럼 입력하면 access_token을 변수로 저장할 수 있다.

$ export access_token=$(curl -X POST http://localhost:8180/realms/myrealm/protocol/openid-connect/token \ --user mybackend:{CLIENT_SECRET} -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=alice&password=alice&grant_type=password' | jq -r ".access_token")

 

access_token값을 갖고 다음과 같이 backend 서버에 요청을 보내 접근을 시도해보면 정상적으로 응답이 오는 것을 확인할 수 있다.

 

$ curl -X GET http://localhost:8080 -H 'Authorization: Bearer '$access_token

 

 

 

 

4. 리버스 프록시 활용

 

최근 프록시들은 OpenID Connect 및 OAuth2를 지원하는 것이 거의 필수적이다. 대표적으로 아파치 HTTP 서버, nginx는 이런 프로토콜에 대한 확장 기능을 제공한다. 이번 절에서는 애플리케이션 앞단에 아파치 HTTP 서버를 프록시로 적용하고, Keycloak과 통합하는 방법에 대해서 실습해본다.

 

교재 코드의 ch7/reverse-proxy/secure-proxy.conf에 들어가보면 OIDCClientSecret을 입력하는 부분이 있다. 여기에 mywebapp client의 secret 값을 넣어준다.

 

그리고 app 디렉토리로 이동 후, npm install, npm start 명령어로 애플리케이션을 실행한다. localhost:8000에 접속한다.(책에서는 8080 포트에 접속하라는데, 이번 장에서는 8180 포트를 쓰기로 한데다, 코드상 프록시 서버의 포트는 8000으로 설정되어 있어서 그냥 8000번 으로 접속하여 정상임을 확인하고 마무리하였다.)

 

 

728x90
반응형