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 서비스에서 로그인, 회원가입 토큰 발급과 각 서비스에서 헤더로 전송된 토큰 체크입니다.
728x90