-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(#15): gateway 모듈에서 검증된 토큰 헤더에 추가 후 요청 전달 * feat(#15): User 모듈에 spring security 및 커스텀 필터(헤더 파싱) 추가
- Loading branch information
Showing
16 changed files
with
365 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,53 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.springframework.boot' version '3.3.4' | ||
id 'io.spring.dependency-management' version '1.1.6' | ||
id 'java' | ||
id 'org.springframework.boot' version '3.3.4' | ||
id 'io.spring.dependency-management' version '1.1.6' | ||
} | ||
|
||
group = 'com.sparta.gateway' | ||
version = '0.0.1-SNAPSHOT' | ||
|
||
java { | ||
toolchain { | ||
languageVersion = JavaLanguageVersion.of(17) | ||
} | ||
toolchain { | ||
languageVersion = JavaLanguageVersion.of(17) | ||
} | ||
} | ||
|
||
configurations { | ||
compileOnly { | ||
extendsFrom annotationProcessor | ||
} | ||
compileOnly { | ||
extendsFrom annotationProcessor | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
mavenCentral() | ||
} | ||
|
||
ext { | ||
set('springCloudVersion', "2023.0.3") | ||
set('springCloudVersion', "2023.0.3") | ||
} | ||
|
||
dependencies { | ||
implementation 'org.springframework.cloud:spring-cloud-starter-gateway' | ||
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' | ||
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' | ||
compileOnly 'org.projectlombok:lombok' | ||
annotationProcessor 'org.projectlombok:lombok' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
testImplementation 'io.projectreactor:reactor-test' | ||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' | ||
implementation project(':common:domain') | ||
implementation project(':service:auth:auth_dto') | ||
|
||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
implementation 'org.springframework.cloud:spring-cloud-starter-gateway' | ||
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' | ||
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' | ||
compileOnly 'org.projectlombok:lombok' | ||
annotationProcessor 'org.projectlombok:lombok' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
testImplementation 'io.projectreactor:reactor-test' | ||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' | ||
} | ||
|
||
dependencyManagement { | ||
imports { | ||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" | ||
} | ||
imports { | ||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" | ||
} | ||
} | ||
|
||
tasks.named('test') { | ||
useJUnitPlatform() | ||
useJUnitPlatform() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
service/gateway/server/src/main/java/com/sparta/gateway/server/application/AuthService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.sparta.gateway.server.application; | ||
|
||
import com.sparta.auth.auth_dto.jwt.JwtClaim; | ||
|
||
public interface AuthService { | ||
|
||
JwtClaim verifyToken(String token); | ||
|
||
} |
23 changes: 23 additions & 0 deletions
23
...src/main/java/com/sparta/gateway/server/infrastructure/configuration/AuthFeignConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.sparta.gateway.server.infrastructure.configuration; | ||
|
||
import feign.Logger; | ||
import feign.codec.Decoder; | ||
import org.springframework.beans.factory.ObjectFactory; | ||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; | ||
import org.springframework.cloud.openfeign.support.SpringDecoder; | ||
import org.springframework.context.annotation.Bean; | ||
|
||
public class AuthFeignConfig { | ||
|
||
@Bean | ||
public Decoder feignDecoder() { | ||
ObjectFactory<HttpMessageConverters> messageConverters = HttpMessageConverters::new; | ||
return new SpringDecoder(messageConverters); | ||
} | ||
|
||
@Bean | ||
public Logger.Level feignLoggerLevel() { | ||
return Logger.Level.FULL; | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
...teway/server/src/main/java/com/sparta/gateway/server/infrastructure/feign/AuthClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.sparta.gateway.server.infrastructure.feign; | ||
|
||
import com.sparta.auth.auth_dto.jwt.JwtClaim; | ||
import com.sparta.gateway.server.application.AuthService; | ||
import com.sparta.gateway.server.infrastructure.configuration.AuthFeignConfig; | ||
import org.springframework.cloud.openfeign.FeignClient; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestHeader; | ||
|
||
@FeignClient(name = "auth", configuration = AuthFeignConfig.class) | ||
public interface AuthClient extends AuthService { | ||
|
||
@GetMapping("/internal/auth/verify") | ||
JwtClaim verifyToken(@RequestHeader("Authorization") String token); | ||
|
||
} |
80 changes: 80 additions & 0 deletions
80
...rc/main/java/com/sparta/gateway/server/infrastructure/filter/JwtAuthenticationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.sparta.gateway.server.infrastructure.filter; | ||
|
||
import static com.sparta.common.domain.jwt.JwtGlobalConstant.AUTHORIZATION; | ||
import static com.sparta.common.domain.jwt.JwtGlobalConstant.BEARER_PREFIX; | ||
import static com.sparta.common.domain.jwt.JwtGlobalConstant.X_USER_CLAIMS; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.sparta.auth.auth_dto.jwt.JwtClaim; | ||
import com.sparta.gateway.server.application.AuthService; | ||
import java.net.URLEncoder; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Optional; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.cloud.gateway.filter.GatewayFilterChain; | ||
import org.springframework.cloud.gateway.filter.GlobalFilter; | ||
import org.springframework.context.annotation.Lazy; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import reactor.core.publisher.Mono; | ||
|
||
@Slf4j | ||
@Component | ||
public class JwtAuthenticationFilter implements GlobalFilter { | ||
|
||
private final AuthService authService; | ||
|
||
public JwtAuthenticationFilter(@Lazy AuthService authService) { | ||
this.authService = authService; | ||
} | ||
|
||
@Override | ||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { | ||
String path = exchange.getRequest().getURI().getPath(); | ||
|
||
if (path.startsWith("/api/auth/") || path.startsWith("/api/users/sign-up")) { | ||
return chain.filter(exchange); | ||
} | ||
|
||
Optional<String> token = this.extractToken(exchange); | ||
|
||
if (token.isEmpty()) { | ||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); | ||
return exchange.getResponse().setComplete(); | ||
} | ||
|
||
try { | ||
JwtClaim claims = authService.verifyToken(token.get()); | ||
this.addUserClaimsToHeaders(exchange, claims); | ||
} catch (Exception e) { | ||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); | ||
return exchange.getResponse().setComplete(); | ||
} | ||
return chain.filter(exchange); | ||
} | ||
|
||
private void addUserClaimsToHeaders(ServerWebExchange exchange, JwtClaim claims) { | ||
if (claims != null) { | ||
try { | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
String jsonClaims = objectMapper.writeValueAsString(claims); | ||
exchange.getRequest().mutate() | ||
.header(X_USER_CLAIMS, URLEncoder.encode(jsonClaims, StandardCharsets.UTF_8)) | ||
.build(); | ||
} catch (JsonProcessingException e) { | ||
log.error("Error processing JSON: {}", e.getMessage()); | ||
} | ||
} | ||
} | ||
|
||
private Optional<String> extractToken(ServerWebExchange exchange) { | ||
String header = exchange.getRequest().getHeaders().getFirst(AUTHORIZATION); | ||
if (header != null && header.startsWith(BEARER_PREFIX)) { | ||
return Optional.of(header.substring(BEARER_PREFIX.length())); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
...ser/server/src/main/java/com/sparta/user/infrastructure/configuration/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.sparta.user.infrastructure.configuration; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.sparta.user.infrastructure.filter.SecurityContextFilter; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; | ||
import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
@EnableMethodSecurity | ||
@EnableWebSecurity | ||
@RequiredArgsConstructor | ||
@Configuration | ||
public class SecurityConfig { | ||
|
||
@Bean | ||
public SecurityFilterChain httpSecurity(HttpSecurity http, ObjectMapper objectMapper) | ||
throws Exception { | ||
http | ||
.csrf(AbstractHttpConfigurer::disable) | ||
.formLogin(AbstractHttpConfigurer::disable) | ||
.sessionManagement((s) -> s.sessionCreationPolicy( | ||
SessionCreationPolicy.STATELESS)) | ||
.rememberMe(AbstractHttpConfigurer::disable) | ||
.httpBasic(AbstractHttpConfigurer::disable) | ||
.logout(AbstractHttpConfigurer::disable) | ||
.requestCache(RequestCacheConfigurer::disable) | ||
.authorizeHttpRequests(authorize -> authorize | ||
.requestMatchers("/api/users/sign-up").permitAll() | ||
.requestMatchers("/internal/users/**").permitAll() | ||
.anyRequest().authenticated()) | ||
.addFilterAfter(new SecurityContextFilter(objectMapper), | ||
UsernamePasswordAuthenticationFilter.class); | ||
|
||
return http.build(); | ||
} | ||
|
||
@Bean | ||
public PasswordEncoder getPasswordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
} |
Oops, something went wrong.