Keycloak으로 Spring Boot REST API 보안

개요

Keycloak은 코드가 거의 또는 전혀없이 최신 애플리케이션과 서비스를 쉽게 보호 할 수있는 오픈 소스 ID 및 액세스 관리 솔루션입니다.

Keycloak은 선택한 플랫폼을위한 자체 어댑터와 함께 제공되지만 일반 OpenID Connect 신뢰 당사자 및 SAML 서비스 공급자 라이브러리를 사용할 수도 있습니다. 그러나 Keycloak 클라이언트 어댑터를 사용하는 것은 훨씬 더 간단하고 사용하기 쉬우 며 일반적으로 라이브러리에 필요한 것보다 더 적은 상용구 코드를 필요로합니다.

이 기사의 주요 초점은 Keycloak Spring Boot Adaptor로 Spring Boot REST API를 보호하는 것입니다.

이 자습서를 따르려면 실행중인 Keycloak 인스턴스가 있어야합니다. 없는 경우 이전 Medium 기사를 따르십시오.

Kubernetes를 통한 ID 및 액세스 관리 및 고 가용성 배포를위한 Keycloak

Keycloak 구성

먼저 Keycloak에서 필요한 구성을 만들어 보겠습니다.

영역 생성

영역은 사용자 자격 증명, 역할 및 그룹의 세트를 관리합니다. 사용자가 영역에 속하고 로그인합니다. 영역은 서로 격리되며 자신이 제어하는 ​​사용자 만 관리하고 인증 할 수 있습니다.

  1. 이동 통해 http : // localhost : 8080 / 인증 / 관리 / 및 관리자 자격 증명을 사용하여 Keycloak 관리 콘솔에 로그인합니다.
  2. 로부터 마스터 드롭 다운 메뉴를 클릭 추가 영역을 . 마스터 영역에 로그인하면이 드롭 다운 메뉴에 기존 영역이 모두 나열됩니다.
  3. 입력 Demo-Realm이름 필드를 클릭 만듭니다 .
Keycloak 관리 콘솔에 영역 추가

Demo-Realm아래 구성에 대해 선택되어 있는지 확인하십시오 . 마스터 영역을 사용하지 마십시오. 매번 영역을 만들 필요는 없습니다. 일회성 프로세스입니다.

클라이언트 생성

클라이언트는 사용자를 인증하기 위해 Keycloak을 요청할 수있는 엔티티입니다. 대부분의 경우 클라이언트는 Keycloak을 사용하여 스스로를 보호하고 단일 사인온 솔루션을 제공하려는 애플리케이션 및 서비스입니다. 클라이언트는 Keycloak으로 보호되는 네트워크의 다른 서비스를 안전하게 호출 할 수 있도록 ID 정보 또는 액세스 토큰을 요청하려는 엔티티 일 수도 있습니다.

  1. 왼쪽 창에서 클라이언트 메뉴를 클릭 합니다. 선택한 Realm에 사용 가능한 모든 클라이언트가 여기에 나열됩니다.
Keycloak 관리 콘솔에서 클라이언트 관리
Keycloak 관리 콘솔에 클라이언트 추가

3. 저장 후 원하는 경우 이름과 설명을 클라이언트에 할당 할 수있는 클라이언트 구성 페이지가 표시됩니다.

설정 액세스 유형confidential, 권한 부여 사용 하는 ON, 서비스 계정 사용 에를 ON클릭 저장 .

액세스 유형 : '기밀'로 클라이언트 구성

Credentials 탭 에는 Spring Boot Application Keycloak 구성에 필요한 클라이언트 시크릿 이 표시됩니다 .

클라이언트 자격 증명 탭

4. 클라이언트 역할 탭으로 이동 하여 springboot-microservice역할 정의 를 만듭니다 . 빌드중인 애플리케이션에 사용자 권한이 다른 여러 유형의 사용자가 있다고 상상해보십시오. 예 : 사용자 및 관리자.

  • 일부 API는 사용자 만 액세스 할 수 있습니다.
  • 일부 API는 관리자 만 액세스 할 수 있습니다.
  • 일부 API는 사용자와 관리자가 모두 액세스 할 수 있습니다.
'springboot-microservice'클라이언트 역할
'사용자'역할 추가 및 저장
'관리자'역할 추가 및 저장
'user', 'admin'역할을 추가 한 후 'springboot-microservice'클라이언트 역할

영역 역할 생성

응용 프로그램은 사용자를 처리하는 것이 너무 세밀하고 관리하기 어려울 수 있으므로 개별 사용자가 아닌 특정 역할에 액세스 및 권한을 할당하는 경우가 많습니다.

해당 역할 ( , ) 을 할당 하여 app-userapp-adminRealm 역할을 생성 해 보겠습니다 .springboot-microserviceuseradmin

  1. 왼쪽 창에서 역할 메뉴를 클릭 합니다. 선택한 Realm에 대해 사용 가능한 모든 역할이 여기에 나열됩니다.
Keycloak 관리 콘솔의 영역 역할
'앱 사용자'영역 역할 추가

저장복합 역할을 활성화 하고 클라이언트 역할 필드 springboot-microservice에서 검색 합니다. 의 역할을 선택 하고 선택한 항목 추가>를 클릭 합니다.userspringboot-microservice

'앱 사용자'영역 역할에 '사용자'클라이언트 역할 할당

이 구성은 영역 역할에 springboot-microservice user클라이언트 역할을 할당합니다 app-user. 여러 역할을 가진 여러 클라이언트가있는 경우 각 클라이언트에서 필요한 역할을 선택하고 선택하여 필요에 따라 영역 역할을 만듭니다.

3. 동일한 단계에 따라 app-admin사용자 를 생성 하되 admin역할 대신 클라이언트 역할을 할당합니다 user.

'app-admin'영역 역할에 'admin'클라이언트 역할 할당

사용자 생성

사용자는 시스템에 로그인 할 수있는 엔티티입니다. 이메일, 사용자 이름, 주소, 전화 번호, 생일 등 자신과 관련된 속성을 가질 수 있습니다. 그룹 멤버십을 지정하고 특정 역할을 지정할 수 있습니다.

하자 다음 사용자를 생성하고 부여 app-userapp-admin목적을 테스트하기위한 역할을.

  • app-user영역 역할이있는 employee1
  • app-admin영역 역할이있는 employee2
  • app-user& app-admin영역 역할이있는 employee3
  • 메뉴에서 사용자 를 클릭 하여 사용자 목록 페이지를 엽니 다.
  • 빈 사용자 목록의 오른쪽에서 사용자 추가를 클릭 하여 사용자 추가 페이지를 엽니 다.
  • 필드에 이름을 입력하십시오 Username. 이것은 유일한 필수 필드입니다. 플립 이메일 확인의 에서 스위치 오프에를 클릭 저장 데이터를 저장하고 새 사용자에 대한 관리 페이지를 엽니 다.
'Demo-Realm'에 새 사용자 추가

5. 새 암호를 입력하고 확인합니다. 플립 임시 전환 에 온 오프를 클릭 암호 재설정을 사용자가 지정한 새에 사용자 암호를 설정할 수 있습니다. 간단하게 mypassword모든 사용자 의 암호를 로 설정하겠습니다 .

사용자에게 자격 증명 설정

6. 역할 매핑 탭을 클릭하여 사용자에게 영역 역할을 할당합니다. 영역 역할 목록은 사용 가능한 역할 목록 에서 사용할 수 있습니다 . 하나의 필수 역할을 선택하고 선택한 항목 추가> 를 클릭 하여 사용자에게 할당합니다.

역할 할당 후 할당 된 역할 목록 에서 할당 된 역할을 사용할 수 있습니다 . 에 대한 역할 할당은 employee1, employee2그리고 employee3다음과 같이 될 것이다.

`employee1` 역할 할당
`employee2` 역할 할당
`employee3` 역할 할당

예, 모든 구성을 수행하는 것은 약간의 번거 로움이었습니다. 그러나 Keycloak을 계속 사용하면 이러한 구성이 케이크 조각이 될 것입니다. 새로운 마이크로 서비스가 추가되기 위해 위의 모든 작업을 수행 할 필요는 없습니다. 클라이언트 역할이있는 새 클라이언트를 추가하고 해당 영역 역할에 클라이언트 역할을 할당하기 만하면됩니다.

토큰 생성

Keycloak 사용자를위한 액세스 토큰을 생성하는 방법을 알아 보겠습니다.

  1. 왼쪽 메뉴에서의 영역 설정 으로 이동하고 OpenID 엔드 포인트 구성Demo-Realm클릭하여 OpenID 엔드 포인트 세부 사항을보십시오.
'데모 영역'의 영역 설정
Keycloak Realm OpenID 끝점 구성

<KEYCLOAK_SERVER_URL>/auth/realms/<REALM_NAME>/protocol/openid-connect/token
Ex: http://localhost:8080/auth/realms/Demo-Realm/protocol/openid-connect/token

curl -X POST '<KEYCLOAK_SERVER_URL>/auth/realms/<REALM_NAME>/protocol/openid-connect/token' \
 --header 'Content-Type: application/x-www-form-urlencoded' \
 --data-urlencode 'grant_type=password' \
 --data-urlencode 'client_id=<CLIENT_ID>' \
 --data-urlencode 'client_secret=<CLIENT_SECRET>' \
 --data-urlencode 'username=<USERNAME>' \
 --data-urlencode 'password=<PASSWORD>'

curl -X POST 'http://localhost:8080/auth/realms/Demo-Realm/protocol/openid-connect/token' \
 --header 'Content-Type: application/x-www-form-urlencoded' \
 --data-urlencode 'grant_type=password' \
 --data-urlencode 'client_id=springboot-microservice' \
 --data-urlencode 'client_secret=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx' \
 --data-urlencode 'username=employee1' \
 --data-urlencode 'password=mypassword'
Postman을 사용하여 토큰 받기

https://jwt.io 를 사용 하기 위해 발급 된 access_token JWT 토큰을 디코딩 해 보겠습니다 .employee1

access_token 에는 권한 세부 정보가 포함됩니다.

  • realm_access.roles 에는 app_user영역 역할이 포함됩니다 .
  • resource_access.springboot-microservice.roles 에는 user클라이언트 역할이 포함됩니다 .
  • preferred_username 에는 사용자 ( employee1) 의 사용자 이름이 포함됩니다.
iat, exp 에는 토큰 발행 시간과 토큰 만료 시간이 포함됩니다. 액세스 토큰 만료 시간은 영역 설정 , 토큰 탭 에서 사용자 정의 할 수 있습니다 . 기본적으로 액세스 토큰 수명 은 보안 요구 사항에 따라 사용자 지정할 수있는 5 분으로 설정됩니다.

Spring Boot 애플리케이션 구성

새로운 Spring Boot 애플리케이션을 빌드하고 Keycloak Spring Boot Adaptor로 구성 해 보겠습니다.

Spring Boot 애플리케이션 생성

초기 프로젝트 구조를 생성하려면 Spring Initializr ( https://start.spring.io/)를 방문하십시오.

아래와 같이 세부 정보를 제공하십시오.

  • 프로젝트 : Maven 프로젝트
  • 언어 : 자바
  • Spring Boot : 안정적인 최신 버전을 선택하거나 기본 선택을 그대로 유지합니다.
  • 프로젝트 메타 데이터 : 아티팩트 이름을 제공하고 선호하는 Java 버전을 선택하십시오. 로컬 환경에서 선택한 Java 버전을 사용할 수 있는지 확인하십시오. 다운로드하여 설치하지 않는 경우.
  • 종속성 : Spring Web, Spring Security 및 Spring Boot DevTools 추가

IntelliJ IDEA, Eclipse와 같은 선호하는 Java 개발 IDE에서 엽니 다.

pom.xml 변경 사항

pom.xml을 열고 다음과 같이 변경하십시오.

  1. Keycloak 버전 속성 추가

<properties>
   <java.version>11</java.version>
   <keycloak.version>9.0.2</keycloak.version>
</properties>

<dependencies> 섹션을 찾아 keycloak-spring-boot-starter.

<dependencies>
   <dependency>
      <groupId>org.keycloak</groupId>
      <artifactId>keycloak-spring-boot-starter</artifactId>
      <version>${keycloak.version}</version>
   </dependency>
   ...
</dependencies>

<dependencies> 섹션 아래에 섹션을 추가하십시오.

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>org.keycloak.bom</groupId>
         <artifactId>keycloak-adapter-bom</artifactId>
         <version>${keycloak.version}</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>

application.properties

application.propertiessrc / main / resources에서 열고 필요한 Keycloak 구성을 제공합니다.

server.port                         = 8000

keycloak.realm                      = <REALM_NAME>
keycloak.auth-server-url            = <KEYCLOAK_SERVER_URL>/auth
keycloak.ssl-required               = external
keycloak.resource                   = <CLIENT_ID>
keycloak.credentials.secret         = <CLIENT_SECRET>
keycloak.use-resource-role-mappings = true
keycloak.bearer-only                = true

server.port                         = 8000

keycloak.realm                      = Demo-Realm
keycloak.auth-server-url            = http://localhost:8080/auth
keycloak.ssl-required               = external
keycloak.resource                   = springboot-microservice
keycloak.credentials.secret         = XXXXXXXXXXXXXXXXXXXXXXXXX
keycloak.use-resource-role-mappings = true
keycloak.bearer-only                = true

Keycloak은 WebSecurityConfigurer 인스턴스 를 만들기위한 편리한 기본 클래스로 KeycloakWebSecurityConfigurerAdapter를 제공 합니다. 구현은 메서드를 재정 의하여 사용자 지정을 허용합니다. 필수는 아니지만 보안 컨텍스트 구성을 크게 단순화합니다.

이제 만들어 보자 KeycloakSecurityConfig.javaconfig패키지로 제공된다.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
            .anyRequest()
            .permitAll();
        http.csrf().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public KeycloakConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

sessionAuthenticationStrategy : 세션 인증 전략을 정의합니다.

KeycloakConfigResolver : 기본적으로 Spring Security Adapter는 keycloak.json구성 파일을 찾습니다 . 이 bean을 추가하여 Spring Boot Adapter가 제공하는 구성을 확인할 수 있습니다.

@EnableGlobalMethodSecurity : jsr250Enabled의 속성은 우리가 사용할 수 있습니다 @RoleAllowed 주석을. 다음 섹션에서이 주석에 대해 자세히 살펴 보겠습니다.

TestController.java

API 보안을 테스트하려면 더미 API가 필요합니다.

패키지 TestController.java에서 만듭니다 controller.

@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping(value = "/anonymous", method = RequestMethod.GET)
    public ResponseEntity<String> getAnonymous() {
        return ResponseEntity.ok("Hello Anonymous");
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ResponseEntity<String> getUser() {
        return ResponseEntity.ok("Hello User");
    }

    @RequestMapping(value = "/admin", method = RequestMethod.GET)
    public ResponseEntity<String> getAdmin() {
        return ResponseEntity.ok("Hello Admin");
    }

    @RequestMapping(value = "/all-user", method = RequestMethod.GET)
    public ResponseEntity<String> getAllUser() {
        return ResponseEntity.ok("Hello All User");
    }
}

mvn spring-boot:run

curl -X GET 'http://localhost:8000/test/anonymous'
curl -X GET 'http://localhost:8000/test/user'
curl -X GET 'http://localhost:8000/test/admin'
curl -X GET 'http://localhost:8000/test/all-user'

보시다시피 모든 API에는 인증이나 승인이 필요하지 않습니다. 이제 이러한 API 엔드 포인트를 보호 해 보겠습니다.

@RolesAllowed 주석으로 역할 기반 액세스 정의

사용 @RolesAllowed주석이 허용 된 사용자 역할을 정의 할 수 있습니다.

/ test / anonymous :

이 API는 제한없이 인증 토큰없이 액세스 할 수 있어야합니다. 이미 우리의 요구 사항을 충족하며 추가 변경이 필요하지 않습니다.

/ 테스트 / 사용자 :

이 API는 springboot-microservice user역할 이있는 사용자가 액세스 할 수 있어야합니다 . 아래와 같이 코드를 변경하여 정의 할 수 있습니다.

@RolesAllowed("user")
@RequestMapping(value = "/user", method = RequestMethod.GET)
public ResponseEntity<String> getUser(@RequestHeader String Authorization) {
    return ResponseEntity.ok("Hello User");
}

이 API는 springboot-microservice admin역할 이있는 사용자가 액세스 할 수 있어야합니다 . 아래와 같이 코드를 변경하여 정의 할 수 있습니다.

@RolesAllowed("admin")
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public ResponseEntity<String> getAdmin(@RequestHeader String Authorization) {
    return ResponseEntity.ok("Hello Admin");
}

이 API는 springboot-microservice user& admin역할을 가진 사용자가 액세스 할 수 있어야 합니다. 아래와 같이 코드를 변경하여 정의 할 수 있습니다.

@RolesAllowed({ "admin", "user" })
@RequestMapping(value = "/all-user", method = RequestMethod.GET)
public ResponseEntity<String> getAllUser(@RequestHeader String Authorization) {
    return ResponseEntity.ok("Hello All User");
}

@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping(value = "/anonymous", method = RequestMethod.GET)
    public ResponseEntity<String> getAnonymous() {
        return ResponseEntity.ok("Hello Anonymous");
    }

    @RolesAllowed("user")
    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ResponseEntity<String> getUser(@RequestHeader String Authorization) {
        return ResponseEntity.ok("Hello User");
    }

    @RolesAllowed("admin")
    @RequestMapping(value = "/admin", method = RequestMethod.GET)
    public ResponseEntity<String> getAdmin(@RequestHeader String Authorization) {
        return ResponseEntity.ok("Hello Admin");
    }

    @RolesAllowed({ "admin", "user" })
    @RequestMapping(value = "/all-user", method = RequestMethod.GET)
    public ResponseEntity<String> getAllUser(@RequestHeader String Authorization) {
        return ResponseEntity.ok("Hello All User");
    }

}

curl -X GET 'http://localhost:8000/test/user' \
--header 'Authorization: bearer <ACCESS_TOKEN>'
Outputs:
anonymous: 403 Forbidden
employee1: Hello User
employee2: 403 Forbidden
employee3: Hello User
curl -X GET 'http://localhost:8000/test/admin' \
--header 'Authorization: bearer <ACCESS_TOKEN>'
Outputs:
anonymous: 403 Forbidden
employee1: 403 Forbidden
employee2: Hello Admin
employee3: Hello Admin
curl -X GET 'http://localhost:8000/test/all-user' \
--header 'Authorization: bearer <ACCESS_TOKEN>'
Outputs:
anonymous: 403 Forbidden
employee1: Hello All User
employee2: Hello All User
employee3: Hello All User

보안 구성으로 역할 기반 액세스 정의

@RolesAllowed 주석을 사용하는 대신 아래와 같이 KeycloakSecurityConfig 클래스에서 동일한 구성을 만들 수 있습니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    super.configure(http);
    http.authorizeRequests()
        .antMatchers("/test/anonymous").permitAll()
        .antMatchers("/test/user").hasAnyRole("user")
        .antMatchers("/test/admin").hasAnyRole("admin")
        .antMatchers("/test/all-user").hasAnyRole("user","admin")
        .anyRequest()
        .permitAll();
    http.csrf().disable();
}

다음 기사에서는 Keycloak을 사용하여 Node.js 기반 REST API 보안을 논의하겠습니다.

Suggested posts

AWS Java Runtime Interface 클라이언트 소싱 열기

AWS Java Runtime Interface 클라이언트 소싱 열기

re : Invent 2020에서 AWS Lambda 함수를위한 새로운 패키징 형식이 도입되었습니다. 컨테이너 이미지. 이 기능을 통해 고객도 사용할 수있는 관리 형 기본 이미지가 많이 생겼습니다.

API 보안이 중요한 이유는 무엇입니까?

API 보안이 중요한 이유는 무엇입니까?

API는 모든 디지털 혁신의 기본이며, 요즘 거의 모든 조직이 클라우드 여정 및 마이크로 서비스 아키텍처를 위해 노력하고 있습니다. 실용적인 목적 (PHI, SPI, Legal 등)

Related posts

"실용적인 프로그래머"의 5 가지 필수 사항

역대 베스트셀러 코딩 북의 요점

"실용적인 프로그래머"의 5 가지 필수 사항

Pragmatic Programmer는 1999 년에 처음 출판되었으며 이후 역대 최고의 프로그래밍 책으로 선정되었습니다. 저자 Andy Hunt와 David Thomas는 Agile Manifesto의 원저자 중 하나였으며 몇 가지 심각한 자격을 가지고 있습니다.

대규모 GraphQL 쿼리 공격으로부터 보호

공격자가 공개적으로 사용 가능한 GraphQL 인터페이스를 사용하여 사이트를 스크랩하거나 서비스 거부 공격을 실행하는 방법에 대해 알아보십시오. 이들은 4 가지 방법 중 하나로이를 수행 할 수 있습니다. 단일 대형 쿼리를 신중하게 구성하여 실행하고, 관련 데이터를 가져올 수있는 병렬 쿼리를 많이 작성하고, 일괄 요청을 사용하여 많은 쿼리를 연속적으로 실행하고, 마지막으로 많은 요청을 보냅니다.

기술 인터뷰의 사회적 구성 요소

코딩 문제는 스트레스가 많지만 스트레스에 대한 당신의 반응은 당신의 기술적 능력보다 더 크게 말합니다.

기술 인터뷰의 사회적 구성 요소

기술 업계의 직책을 위해 인터뷰 할 때 일반적으로 제안을 고려하기 전에 최소한 3 차례의 인터뷰를 거치게됩니다. 라운드는 일반적으로 다음과 같습니다. 그렇게 생각하면 잘못된 것입니다.

훌륭한 개발자의 3 가지 행동 특성

훌륭한 개발자의 3 가지 행동 특성

훌륭한 개발자를 만드는 비 기술적 인 것들 나는이 기사를 작성하는 것을 한동안 미루고 있습니다. 나는 그것을 작성할 자격이 있다고 생각하지 못했습니다. 오늘은 쓸 때라고 생각했습니다.