Spring

[Keycloak] OAuth2 MSA에서 키클록 SSO Spring Security 연동 (1) dependency 추가, gateway/auth 서비스 설정

Karla Ko 2022. 12. 27. 12:28
728x90

개발환경

  • macOS
  • Spring Boot 2.7.5 RELEASE
  • JAVA 11

 


1. MSA 서비스 구조

서비스 디스커버리 패턴으로 gateway 서비스(api gateway)를 통해 각 서비스의 api를 호출하는 형태입니다.
auth 서비스를 통해 회원가입/로그인 등 계정 및 권한을 관리합니다. (키클록 토큰 발급)

 

 

2. gateway 서비스 설정

0) 프로젝트 구조

📦gatewayserver
┣ 📂src
┃┣ 📂main
┃ ┃ ┣ 📂java
┃ ┃ ┃ ┗ 📂com
┃ ┃ ┃ ┃ ┗ 📂cloud
┃ ┃ ┃ ┃ ┃ ┗ 📂gatewayserver
┃ ┃ ┃ ┃ ┃ ┃ ┣ 📜GatewayserverApplication.java
┃ ┃ ┃ ┃ ┃ ┃ ┗ 📜SecurityConfig.java
┃ ┃ ┗ 📂resources
┃ ┃ ┃ ┗ 📜application.yml
┗ 📜pom.xml

 

1) pom.xml

spring-boot-starter-oauth2-client 추가

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
</dependencies>

dependencyManagement에 keycloak-adapter-bom 추가

<dependency>
    <groupId>org.keycloak.bom</groupId>
    <artifactId>keycloak-adapter-bom</artifactId>
    <version>17.0.1</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

 

2) application.yml

  • spring security 설정 추가
spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            issuer-uri: http://localhost:5010/realms/team_cloud
            # issuer-uri: http://설정한 키클록url:port/realms/ream 이름
        registration:
          keycloak:
            client-id: team_cloud_client
            # client-id: client 이름
            client-secret: #keycloak client 비밀번호
            authorization-grant-type: authorization_code
            redirect-uri: '{baseUrl}/login/oauth2/code/keycloak'

 

  • keycloak 설정 추가
keycloak:
  realm: team_cloud
  # realm: realm 이름
  resource: team_cloud_client
  # resource: client 이름
  auth-server-url: http://localhost:5010/
  # auth-server-url: http://설정한 키클록url:port/

 

3) SecurityConfig.java 생성

package com.cloud.gatewayserver;


import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange().pathMatchers("/**").permitAll()
                .and().authorizeExchange().anyExchange().authenticated()
                .and().oauth2Login()
                .and().csrf().disable();

        return http.build();
    }
}

 

3. 서비스 설정 (security config 파일  추가)

  • 각 서비스에 config 파일을 추가해야함
📦survey
 ┣ 📂src
 ┃ ┣ 📂main
 ┃ ┃ ┣ 📂java
 ┃ ┃ ┃ ┗ 📂com
 ┃ ┃ ┃ ┃ ┗ 📂cloud
 ┃ ┃ ┃ ┃ ┃ ┗ 📂survey
 ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📂config
 ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📜SurveyConfig.java
 ┃ ┃ ┃ ┃ ┃ ┃ ┣ ...
 ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📜SurveyApplication.java
 ┃ ┃ ┗ 📂resources
 ┃ ┃ ┃ ┣ 📜application.yml
 ┗ 📜pom.xml

1) pom.xml

  • spring-boot-starter-oauth2-resource-server
  • spring-boot-starter-security
  • keycloak-spring-boot-starter dependency
<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>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>17.0.1</version>
</dependency>
  • dependencyManagement에 keycloak-adapter-bom
<dependency>
    <groupId>org.keycloak.bom</groupId>
    <artifactId>keycloak-adapter-bom</artifactId>
    <version>17.0.1</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

 

2)application.yml

  • spring security 설정 추가
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://localhost:5010/realms/team_cloud/protocol/openid-connect/certs
          # jwk-set-uri: http://설정한 키클록url:port/realms/team_cloud/protocol/openid-connect/certs

 

  • keycloak 설정 추가
keycloak:
  realm: team_cloud
  # realm: realm 이름
  bearer-only: true
  ssl-required: external
  resource: team_cloud_client
  # resource: client 이름
  auth-server-url: http://localhost:5010/
  #  auth-server-url: ttp://설정한 키클록url:port/
  credentials:
    secret: # keycloak client 비밀번호

 

3) SurveyConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SurveyConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .antMatchers("/v1/survey/**", "/v1/answer/**").permitAll()
                .and().oauth2ResourceServer()
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
                .and().csrf().disable()
        ;
}

    private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter() {
        JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
        jwtConverter.setJwtGrantedAuthoritiesConverter(new RealmRoleConverter());
        return jwtConverter;
    }

    public class RealmRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
        @Override
        public Collection<GrantedAuthority> convert(Jwt jwt) {
            final Map<String, List<String>> realmAccess = (Map<String, List<String>>) jwt.getClaims().get("realm_access");
            return realmAccess.get("roles").stream().map(roleName -> "ROLE_" + roleName)
                    .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        }
    }
}

 

4. auth 서비스 추가 설정 (keycloak-admin-client)

0. 프로젝트 구조

📦auth
 ┣ 📂src
 ┃ ┣ 📂main
 ┃ ┃ ┣ 📂java
 ┃ ┃ ┃ ┗ 📂com
 ┃ ┃ ┃ ┃ ┗ 📂cloud
 ┃ ┃ ┃ ┃ ┃ ┗ 📂auth
 ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📂config
 ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📜AuthConfig.java
 ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📜KeycloakConfiguration.java
 ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📜MapperConfig.java
 ┃ ┃ ┃ ┃ ┃ ┃ ┣ ...
 ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📜AuthApplication.java
 ┃ ┃ ┃ ┗ 📜application.yml
 ┗ 📜pom.xml

 

1) pom.xml

  • spring-boot-starter-oauth2-resource-server(모든 서비스에 추가)
  • spring-boot-starter-security(모든 서비스에 추가)
  • keycloak-spring-boot-starter (모든 서비스에 추가) 
  • keycloak-admin-client (auth 서비스에 추가)
<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>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>17.0.1</version>
</dependency>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-admin-client</artifactId>
    <version>17.0.1</version>
</dependency>

 

  • dependencyManagement에 keycloak-adapter-bom (모든 서비스에 추가) 추가
<dependency>
    <groupId>org.keycloak.bom</groupId>
    <artifactId>keycloak-adapter-bom</artifactId>
    <version>17.0.1</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

 

2) AuthConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class AuthConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**").permitAll()
                .and().oauth2ResourceServer()
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
                .and().csrf().disable()

        ;
    }

    private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter() {
        JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
        jwtConverter.setJwtGrantedAuthoritiesConverter(new RealmRoleConverter());
        return jwtConverter;
    }

    public class RealmRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
        @Override
        public Collection<GrantedAuthority> convert(Jwt jwt) {
            final Map<String, List<String>> realmAccess = (Map<String, List<String>>) jwt.getClaims().get("realm_access");
            return realmAccess.get("roles").stream().map(roleName -> "ROLE_" + roleName)
                    .map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        }
    }
}

 

3) KeycloakConfiguration.java

@Configuration
public class KeycloakConfiguration {
    @Value("${keycloak.auth-server-url}")
    private String authServerUrl;

    @Value("${keycloak.realm}")
    private String realm;

    @Value("${keycloak.resource}")
    private String clientId;

    @Value("${keycloak.credentials.secret}")
    private String clientSecret;

    /*
     *  keycloak.json 대신에 Spring Boot yml 파일을 이용하도록 돕는다.
     * */
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    /*
     *  Keycloak 서버와 통신하기 위한 클라이언트 빌더
     * */
    @Bean
    public Keycloak keycloak() {
        return KeycloakBuilder.builder()
                .serverUrl(authServerUrl)
                .realm(realm)
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .clientId(clientId)
                .clientSecret(clientSecret)
                .build();
    }
}

 


이렇게 설정은 끝입니다.

다음 포스팅으로 auth 서비스에서 로그인, 회원가입 토큰 발급과 각 서비스에서 헤더로 전송된 토큰 체크입니다.

 

[Keycloak] MSA에서 키클록 Spring Security 연동 (2) 토큰 발급, 토큰값 확인

[Keycloak] MSA에서 키클록 Spring Security 연동 (1) dependency 추가, gateway/auth 서비스 설정 개발환경 macOS Spring Boot 2.7.5 RELEASE JAVA 11 1. MSA 서비스 구조 서비스 디스커버리 패턴으로 gateway 서비스(api gateway)를

karla.tistory.com

 

728x90