diff --git a/build.gradle b/build.gradle index cc423ad..95fe2bd 100644 --- a/build.gradle +++ b/build.gradle @@ -43,14 +43,6 @@ dependencies { implementation("com.google.firebase:firebase-admin:9.7.0") { exclude group: "commons-logging", module: "commons-logging" } - - // Spring Security - implementation 'org.springframework.boot:spring-boot-starter-security' - - // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.12.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5' } protobuf { diff --git a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationFilter.java b/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationFilter.java deleted file mode 100644 index b03a505..0000000 --- a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.pinitnotification.infrastructure.authenticate; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -public class JwtAuthenticationFilter extends OncePerRequestFilter { - - private final AuthenticationManager authenticationManager; - - public JwtAuthenticationFilter(AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - try { - String token = resolveToken(request); - - if (StringUtils.hasText(token)) { - JwtAuthenticationToken authenticationToken = new JwtAuthenticationToken(token); - Authentication authentication = authenticationManager.authenticate(authenticationToken); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - filterChain.doFilter(request, response); - } catch (AuthenticationException ex) { - SecurityContextHolder.clearContext(); - throw ex; - } - } - - private String resolveToken(HttpServletRequest request) { - String bearerToken = request.getHeader("Authorization"); - if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { - return bearerToken.substring(7); - } - return null; - } -} - diff --git a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationProvider.java b/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationProvider.java deleted file mode 100644 index 22968ed..0000000 --- a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.pinitnotification.infrastructure.authenticate; - -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; - -public class JwtAuthenticationProvider implements AuthenticationProvider { - private final JwtTokenProvider jwtTokenProvider; - - public JwtAuthenticationProvider(JwtTokenProvider jwtTokenProvider) { - this.jwtTokenProvider = jwtTokenProvider; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if(!(authentication instanceof JwtAuthenticationToken)){ - return null; - } - - String token = (String) authentication.getCredentials(); - if(!jwtTokenProvider.validateToken(token)) { - throw new BadCredentialsException("Invalid token"); - } - - Long memberId = jwtTokenProvider.getMemberId(token); - Collection authorities = jwtTokenProvider.getAuthorities(token); - - return new JwtAuthenticationToken(memberId, token, authorities); - } - - @Override - public boolean supports(Class authentication) { - return JwtAuthenticationToken.class.isAssignableFrom(authentication); - } -} - diff --git a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationToken.java b/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationToken.java deleted file mode 100644 index 26a80e3..0000000 --- a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtAuthenticationToken.java +++ /dev/null @@ -1,45 +0,0 @@ -package me.pinitnotification.infrastructure.authenticate; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; -import java.util.Collections; - -public class JwtAuthenticationToken extends AbstractAuthenticationToken { - private final Long memberId; - private final String token; - - public JwtAuthenticationToken(String token) { - super(Collections.emptyList()); - this.memberId = null; - this.token = token; - super.setAuthenticated(false); - } - - public JwtAuthenticationToken(Long principal, String token, Collection authorities) { - super(authorities); - this.memberId = principal; - this.token = token; - super.setAuthenticated(true); - } - - @Override - public String getCredentials() { - return token; - } - - @Override - public Long getPrincipal() { - return memberId; - } - - @Override - public void setAuthenticated(boolean isAuthenticated) { - if (isAuthenticated) { - throw new IllegalArgumentException("인증 상태 설정은 생성자에서만 할 수 있습니다."); - } - super.setAuthenticated(false); - } - -} diff --git a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtTokenProvider.java b/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtTokenProvider.java deleted file mode 100644 index e24a906..0000000 --- a/src/main/java/me/pinitnotification/infrastructure/authenticate/JwtTokenProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -package me.pinitnotification.infrastructure.authenticate; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.JwtParser; -import io.jsonwebtoken.Jwts; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; - -import java.security.PublicKey; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.stream.Collectors; - -public class JwtTokenProvider { - private final PublicKey publicKey; - - public JwtTokenProvider(PublicKey publicKey) { - this.publicKey = publicKey; - } - - public boolean validateToken(String token) { - try { - Claims claims = parse(token); - return !claims.getExpiration().before(new Date()); - } catch (Exception e) { - return false; - } - } - - public Long getMemberId(String token) { - return Long.parseLong(parse(token).getSubject()); - } - - public Claims parse(String token){ - return buildParser() - .parseSignedClaims(token) - .getPayload(); - } - - public Collection getAuthorities(String token) { - Claims claims = parse(token); - String roles = claims.get("roles", String.class); - if (roles == null || roles.isEmpty()) { - return Collections.emptyList(); - } - return Arrays.stream(roles.split(",")) - .map(String::trim) - .filter(r -> !r.isEmpty()) - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); - } - - private JwtParser buildParser() { - return Jwts.parser() - .verifyWith(publicKey) - .build(); - } -} - diff --git a/src/main/java/me/pinitnotification/infrastructure/authenticate/RsaKeyProvider.java b/src/main/java/me/pinitnotification/infrastructure/authenticate/RsaKeyProvider.java deleted file mode 100644 index cf42a0b..0000000 --- a/src/main/java/me/pinitnotification/infrastructure/authenticate/RsaKeyProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.pinitnotification.infrastructure.authenticate; - -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.PublicKey; -import java.security.spec.X509EncodedKeySpec; -import java.util.Base64; - -public class RsaKeyProvider { - public static PublicKey loadPublicKey(String pemPath) { - try { - String key = new String(Files.readAllBytes(Paths.get(pemPath))); - key = key - .replace("-----BEGIN PUBLIC KEY-----", "") - .replace("-----END PUBLIC KEY-----", "") - .replaceAll("\\s", ""); - - byte[] decoded = Base64.getDecoder().decode(key); - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded); - - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePublic(keySpec); - } catch (IOException | GeneralSecurityException e) { - throw new IllegalStateException("Failed to load RSA public key", e); - } - } -} - diff --git a/src/main/java/me/pinitnotification/infrastructure/authenticate/config/SecurityConfig.java b/src/main/java/me/pinitnotification/infrastructure/authenticate/config/SecurityConfig.java deleted file mode 100644 index 0102ee8..0000000 --- a/src/main/java/me/pinitnotification/infrastructure/authenticate/config/SecurityConfig.java +++ /dev/null @@ -1,70 +0,0 @@ -package me.pinitnotification.infrastructure.authenticate.config; - -import jakarta.servlet.http.HttpServletResponse; -import me.pinitnotification.infrastructure.authenticate.JwtAuthenticationFilter; -import me.pinitnotification.infrastructure.authenticate.JwtAuthenticationProvider; -import me.pinitnotification.infrastructure.authenticate.JwtTokenProvider; -import me.pinitnotification.infrastructure.authenticate.RsaKeyProvider; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -import java.security.PublicKey; - -@Configuration -public class SecurityConfig { - - @Value("${path.key.jwt.public}") - private String publicKeyPath; - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { - http - .cors(Customizer.withDefaults()) - .csrf(AbstractHttpConfigurer::disable) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .exceptionHandling(e -> e.authenticationEntryPoint( - (request, response, ex) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or missing token") - )) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/actuator/health/liveness", "/actuator/health/readiness", "/v3/**", "/swagger-ui/**", "/async-api/**").permitAll() - .anyRequest().authenticated() - ); - - http.addFilterBefore( - jwtAuthenticationFilter(authenticationManager), - UsernamePasswordAuthenticationFilter.class - ); - - return http.build(); - } - - @Bean - public JwtAuthenticationFilter jwtAuthenticationFilter(AuthenticationManager authenticationManager) { - return new JwtAuthenticationFilter(authenticationManager); - } - - @Bean - public AuthenticationManager authenticationManager(JwtAuthenticationProvider jwtAuthenticationProvider) { - return new ProviderManager(jwtAuthenticationProvider); - } - - @Bean - public JwtAuthenticationProvider jwtAuthenticationProvider(JwtTokenProvider jwtTokenProvider) { - return new JwtAuthenticationProvider(jwtTokenProvider); - } - - @Bean - public JwtTokenProvider jwtTokenProvider() { - PublicKey publicKey = RsaKeyProvider.loadPublicKey(publicKeyPath); - return new JwtTokenProvider(publicKey); - } -} diff --git a/src/main/java/me/pinitnotification/infrastructure/web/MemberIdArgumentResolver.java b/src/main/java/me/pinitnotification/infrastructure/web/MemberIdArgumentResolver.java index bbce8ff..6910fee 100644 --- a/src/main/java/me/pinitnotification/infrastructure/web/MemberIdArgumentResolver.java +++ b/src/main/java/me/pinitnotification/infrastructure/web/MemberIdArgumentResolver.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import me.pinitnotification.domain.member.MemberId; import me.pinitnotification.domain.member.exception.MemberNotFoundException; -import me.pinitnotification.infrastructure.authenticate.JwtTokenProvider; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -15,11 +14,6 @@ @Slf4j @Component public class MemberIdArgumentResolver implements HandlerMethodArgumentResolver { - private final JwtTokenProvider jwtTokenProvider; - - public MemberIdArgumentResolver(JwtTokenProvider jwtTokenProvider) { - this.jwtTokenProvider = jwtTokenProvider; - } @Override public boolean supportsParameter(MethodParameter parameter) { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 0d32ff7..bebe196 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -24,6 +24,4 @@ spring: path: key: - firebase: ${HOME}/pinit/keys/pinit-firebase-key.json - jwt: - public: ${HOME}/pinit/keys/jwt-public-key.pem \ No newline at end of file + firebase: ${HOME}/pinit/keys/pinit-firebase-key.json \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index cc99898..48a461c 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -22,6 +22,4 @@ spring: path: key: - firebase: /etc/keys/pinit-firebase-key.json - jwt: - public: /etc/keys/jwt-public-key.pem \ No newline at end of file + firebase: /etc/keys/pinit-firebase-key.json \ No newline at end of file diff --git a/src/test/java/me/pinitnotification/PinitNotificationApplicationTests.java b/src/test/java/me/pinitnotification/PinitNotificationApplicationTests.java index c6e2dcb..edb10a5 100644 --- a/src/test/java/me/pinitnotification/PinitNotificationApplicationTests.java +++ b/src/test/java/me/pinitnotification/PinitNotificationApplicationTests.java @@ -2,7 +2,6 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.messaging.FirebaseMessaging; -import me.pinitnotification.infrastructure.authenticate.JwtTokenProvider; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -17,8 +16,6 @@ class PinitNotificationApplicationTests { FirebaseApp firebaseApp; @MockitoBean FirebaseMessaging firebaseMessaging; - @MockitoBean - JwtTokenProvider jwtTokenProvider; @Test void contextLoads() { }