JWT Token 기반 인증 인가
JWT Token 기반 인증 인가
JWT 토큰기반 인증 w/ Keycloak
OAuth2 Stackholders
- Spring Security와 Spring oauth2를 사용하고, Resource Owner, Client, Authorization Server, Resource Server간의 인증/인가를 실습한다.
- 여기서 Resouce란 Gateway를 경유하는 Rest APIs를 말한다.
- JWT기반 Access_Token을 활용한다.
- 이번 랩에서는 Gateway를 Client와 Resource Server 역할로 설정한다.
- 인증/인가 서버로 Standalone Keycloak(https://www.keycloak.org/) 서버를 활용한다.
OAuth2 인증/인가(Keycloak) Endpoint 설정
본 예제의 디렉토리로 이동한다:
cd token-based-auth-Keycloak/
- Gateway 서비스의 application.yml 파일을 열어본다.
- 인증/인가를 위한 Authorization Sever의 Endpoint가 등록된다.
security:
oauth2:
client:
provider:
my-keycloak-provider:
issuer-uri: http://localhost:8080/realms/my_realm
- KeyCloak에 등록된 Client(Gateway)의 Credential정보(client-id, client-secret)가 설정된다.
- OAuth2의 Grant Type을 password 방식으로 설정한다.
keycloak-spring-gateway-client:
provider: my-keycloak-provider
client-id: my_client
client-secret: HKFKYP7kb8OMldAgfvnk27FhRPOv8Y7H
authorization-grant-type: password
OAuth2 Security 상세설정
- Gateway 서비스의 SecurityConfig.java 파일을 열어본다.
- spring-cloud-gateway 는 webflux로 기동되기 때문에 @EnableWebFluxSecurity를 적용한다.
- ServerHttpSecurity에 리소스별 접근제어목록(ACL)을 기술한다.
- .oauth2Login() OAuth2의 디폴트 로그인 설정이 적용된다.
- .oauth2ResourceServer() 리소스서버 역할을 부여하고 jwt 형식의 Authorization을 지정한다.
서비스 구동
- 먼저 Keycloak 서버를 구동한다.
cd keycloak/bin
chmod 744 kc.sh
./kc.sh start-dev
- keycloak 서버의 default 포트인 8080으로 실행된다.
- Gateway, Order 서비스를 구동한다.
cd gateway
mvn spring-boot:run
cd order
mvn spring-boot:run
- 각각 8088, 8081 포트로 기동된다.
Protected 리소스 접근
- Security ACL설정(SecurityConfig.java)에 따라 Gateway 서버 및 주문서비스에 접근해 본다.
http http://localhost:8088
http http://localhost:8088/orders
- JWT인증 토큰이 없어 401(Unauthorized) 접근오류 응답이 내려온다.
- 이제는 허가된 리소스에 접근해 본다. (gateway > TestController.java)
http http://localhost:8088/test/permitAll
- 접근 가능하다.
JWT access_token 발급
- Keycloak의 인증/인가 Endpoint에 토큰을 요청한다.
-
OAuth2의 'password' Grant type으로 Keycloak에 기 등록된 Client 크리덴셜과 사용자 정보를 제출한다.
'password' Grant type은 Client(Gateway)의 로그인 Form으로 제출받은 사용자 정보를 인증서버에 Posting하는 방식이다.
curl -X POST "http://localhost:8080/realms/my_realm/protocol/openid-connect/token" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=password" \
--data-urlencode "client_id=my_client" \
--data-urlencode "client_secret=HKFKYP7kb8OMldAgfvnk27FhRPOv8Y7H" \
--data-urlencode "username=user@uengine.org" \
--data-urlencode "password=1"
- 응답으로 access_token과 refresh_token이 내려온다.
-
출력된 access_token을 복사하여 https://jwt.io/ 페이지에 접속 후 decode해 본다.
Header, Payload, Signature로 파싱된다.
- user@uengine.org 계정이 가진 Role은 ROLE_USER임을 확인한다.
access_token으로 Protected 리소스 접근
- access_token을 복사하여 Request Header에 넣어 Protected 리소스에 접근한다.
export access_token=[ACCESS_TOKEN]
echo $access_token
http localhost:8088/orders "Authorization: Bearer $access_token"
http localhost:8088/test/user "Authorization: Bearer $access_token"
http localhost:8088/test/authenticated "Authorization: Bearer $access_token"
http localhost:8088/test/admin "Authorization: Bearer $access_token"
- '/test/admin' 리소스는 권한이 불충분(403 Fobidden)하여 접근할 수 없다.
- 관리자 권한이 있는 계정으로 다시 한번 토큰을 요청한다.
curl -X POST "http://localhost:8080/realms/my_realm/protocol/openid-connect/token" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=password" \
--data-urlencode "client_id=my_client" \
--data-urlencode "client_secret=HKFKYP7kb8OMldAgfvnk27FhRPOv8Y7H" \
--data-urlencode "username=admin@uengine.org" \
--data-urlencode "password=1"
- access_token을 복사하여 Request Header에 넣어 Protected 리소스에 접근한다.
export access_token=[ACCESS_TOKEN]
http localhost:8088/test/admin "Authorization: Bearer $access_token"
- 정상적으로 접근이 가능하다.
Wrap up
- Gateway가 리소스서버 역할까지 수행하므로 각 마이크로서비스 리소스들의 Fine grained한 접근제어를 Gateway에서 관리
- 이로 인해 ACL 정보 가독성이 떨어지거나, ACL 오류발생 시 잠재적 분쟁 소지
- MSA별 Autonomous ACL 관리책임 분산을 위해 인증 및 인가를 분리하는 정책 권고
- Gateway는 인증을 포함한 Coarse grained ACL Policy를 담당하고, 각 MSA에서 Fine grained한 ACL Policy 적용
Service Clear
- 다음 Lab을 위해 기동된 모든 서비스 종료
fuser -k 8080/tcp
fuser -k 8081/tcp
fuser -k 8088/tcp