diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt index 0ffab0299..c6340f197 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilter.kt @@ -3,15 +3,12 @@ package team.aliens.dms.global.filter import jakarta.servlet.FilterChain import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse -import org.springframework.http.HttpMethod import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.util.AntPathMatcher import org.springframework.web.filter.OncePerRequestFilter import team.aliens.dms.domain.auth.model.Authority import team.aliens.dms.domain.auth.model.PassportUser -import team.aliens.dms.global.security.SecurityPaths import team.aliens.dms.global.security.exception.InvalidTokenException import team.aliens.dms.global.security.principle.GeneralTeacherDetails import team.aliens.dms.global.security.principle.HeadTeacherDetails @@ -24,34 +21,26 @@ class JwtAuthenticationFilter( private val jwtParser: JwtParser ) : OncePerRequestFilter() { - private val pathMatcher = AntPathMatcher() - - override fun shouldNotFilter(request: HttpServletRequest): Boolean { - val path = request.requestURI - val method = HttpMethod.valueOf(request.method) - return SecurityPaths.PERMIT_ALL_PATHS.any { permitPath -> - pathMatcher.match(permitPath.path, path) && - (permitPath.method == null || permitPath.method == method) - } - } - override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain ) { - val token = resolveToken(request) - val user = jwtParser.extractUserInfo(token) + val token = resolveTokenOrNull(request) + + if (token != null) { + val user = jwtParser.extractUserInfo(token) - SecurityContextHolder.clearContext() - SecurityContextHolder.getContext().authentication = createAuthentication(user) + SecurityContextHolder.clearContext() + SecurityContextHolder.getContext().authentication = createAuthentication(user) + } filterChain.doFilter(request, response) } - private fun resolveToken(request: HttpServletRequest): String { + private fun resolveTokenOrNull(request: HttpServletRequest): String? { val authorizationHeader = request.getHeader(JwtProperties.HEADER) - ?: throw InvalidTokenException + ?: return null if (!authorizationHeader.startsWith(JwtProperties.PREFIX)) { throw InvalidTokenException diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CustomAuthenticationEntryPoint.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CustomAuthenticationEntryPoint.kt index e29974775..3b6c23812 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CustomAuthenticationEntryPoint.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/CustomAuthenticationEntryPoint.kt @@ -19,7 +19,7 @@ class CustomAuthenticationEntryPoint( authException: AuthenticationException? ) { - val errorCode = SecurityErrorCode.FORBIDDEN + val errorCode = SecurityErrorCode.INVALID_TOKEN response?.apply { contentType = "application/json" diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt index 5b5981c6c..83132d0f4 100644 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt +++ b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityConfig.kt @@ -33,6 +33,9 @@ class SecurityConfig( http .authorizeHttpRequests { authorize -> + authorize + .requestMatchers("/").permitAll() + authorize // /auth .requestMatchers(HttpMethod.GET, "/auth/account-id").permitAll() diff --git a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt b/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt deleted file mode 100644 index abe294cc9..000000000 --- a/dms-main/main-infrastructure/src/main/kotlin/team/aliens/dms/global/security/SecurityPaths.kt +++ /dev/null @@ -1,50 +0,0 @@ -package team.aliens.dms.global.security - -import org.springframework.http.HttpMethod - -object SecurityPaths { - - data class PermitAllPath( - val method: HttpMethod?, - val path: String, - ) - - val PERMIT_ALL_PATHS = listOf( - // healthcheck - PermitAllPath(null, "/"), - - // auth - PermitAllPath(HttpMethod.GET, "/auth/account-id"), - PermitAllPath(HttpMethod.GET, "/auth/email"), - PermitAllPath(HttpMethod.GET, "/auth/code"), - PermitAllPath(HttpMethod.POST, "/auth/code"), - PermitAllPath(HttpMethod.POST, "/auth/tokens"), - PermitAllPath(HttpMethod.PUT, "/auth/reissue"), - PermitAllPath(HttpMethod.POST, "/auth/passport"), - - // students - PermitAllPath(HttpMethod.GET, "/students/email/duplication"), - PermitAllPath(HttpMethod.GET, "/students/account-id/duplication"), - PermitAllPath(HttpMethod.GET, "/students/account-id/{school-id}"), - PermitAllPath(HttpMethod.GET, "/students/name"), - PermitAllPath(HttpMethod.POST, "/students/signup"), - PermitAllPath(HttpMethod.PATCH, "/students/password/initialization"), - - // managers - PermitAllPath(HttpMethod.GET, "/managers/account-id/{school-id}"), - PermitAllPath(HttpMethod.PATCH, "/managers/password/initialization"), - - // schools - PermitAllPath(HttpMethod.GET, "/schools"), - PermitAllPath(HttpMethod.GET, "/schools/question/{school-id}"), - PermitAllPath(HttpMethod.GET, "/schools/answer/{school-id}"), - PermitAllPath(HttpMethod.GET, "/schools/code"), - - // files - PermitAllPath(HttpMethod.POST, "/files"), - PermitAllPath(HttpMethod.GET, "/files/url"), - - // templates (모든 메서드 permitAll) - PermitAllPath(null, "/templates"), - ) -} diff --git a/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt b/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt index f78001bb6..cc20d69ea 100644 --- a/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt +++ b/dms-main/main-infrastructure/src/test/kotlin/team/aliens/dms/global/filter/JwtAuthenticationFilterTest.kt @@ -49,45 +49,25 @@ class JwtAuthenticationFilterTest : DescribeSpec({ val filterChain = mockk(relaxed = true) describe("doFilter") { - context("permitAll 경로면") { - val request = mockRequest(uri = "/auth/tokens", method = "POST", authorizationHeader = null) - - it("JWT 검증 없이 다음 필터로 넘긴다") { - filter.doFilter(request, response, filterChain) - - verify(exactly = 0) { jwtParser.extractUserInfo(any()) } - verify(exactly = 1) { filterChain.doFilter(request, response) } - } - } - - context("permitAll 경로여도 등록된 메서드가 아니면 (/schools/code)") { - it("GET은 JWT 검증 없이 다음 필터로 넘긴다") { - val request = mockRequest(uri = "/schools/code", method = "GET", authorizationHeader = null) - - filter.doFilter(request, response, filterChain) - - verify(exactly = 0) { jwtParser.extractUserInfo(any()) } - verify(exactly = 1) { filterChain.doFilter(request, response) } - } + context("Authorization 헤더가 없으면") { + it("경로·메서드와 무관하게 인증을 세우지 않고 다음 필터로 넘긴다") { + forAll( + row("/auth/tokens", "POST"), + row("/schools/code", "GET"), + row("/schools/code", "PATCH"), + row("/students", "GET"), + ) { uri: String, method: String -> - it("PATCH는 JWT 검증을 한다") { - val request = mockRequest(uri = "/schools/code", method = "PATCH", authorizationHeader = null) + val request = mockRequest(uri = uri, method = method, authorizationHeader = null) - shouldThrow { filter.doFilter(request, response, filterChain) - } - verify(exactly = 0) { filterChain.doFilter(any(), any()) } - } - } - context("인증이 필요한 경로인데 Authorization 헤더가 없으면") { - val request = mockRequest(uri = "/students", authorizationHeader = null) + verify(exactly = 0) { jwtParser.extractUserInfo(any()) } + verify(exactly = 1) { filterChain.doFilter(request, response) } + SecurityContextHolder.getContext().authentication shouldBe null - it("InvalidTokenException을 던지고 다음 필터로 넘어가지 않는다") { - shouldThrow { - filter.doFilter(request, response, filterChain) + SecurityContextHolder.clearContext() } - verify(exactly = 0) { filterChain.doFilter(any(), any()) } } }