feat(auth): короткий access, refresh и 7-дневная sliding-сессия

This commit is contained in:
2026-10-28 13:26:22 +03:00
parent 08e5a62c0e
commit 3adfba9bec
29 changed files with 1261 additions and 14 deletions

View File

@@ -66,6 +66,8 @@ class JwtAuthenticationFilter(
path.startsWith("/actuator") ||
path == "/api/v1/auth/login" ||
path == "/api/v1/auth/setup" ||
path == "/api/v1/auth/refresh" ||
path == "/api/v1/auth/logout" ||
path == "/api/v1/auth/health" ||
path == "/api/v1/auth/jwks.json" ||
path.startsWith("/api/v1/content/")

View File

@@ -0,0 +1,42 @@
package ru.ysu.lms.gateway.security
import io.mockk.mockk
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.springframework.cloud.gateway.filter.GatewayFilterChain
import org.springframework.http.HttpStatus
import org.springframework.mock.http.server.reactive.MockServerHttpRequest
import org.springframework.mock.web.server.MockServerWebExchange
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
class JwtAuthenticationFilterTest {
private val filter = JwtAuthenticationFilter(mockk<JwtDecoder>(relaxed = true))
@Test
fun `refresh and logout are public`() {
listOf("/api/v1/auth/refresh", "/api/v1/auth/logout").forEach { path ->
var called = false
val exchange = MockServerWebExchange.from(MockServerHttpRequest.post(path).build())
filter.filter(exchange, chain { called = true }).block()
assertTrue(called, "gateway did not pass $path")
}
}
@Test
fun `protected route without bearer token is rejected`() {
val exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/api/v1/courses").build())
filter.filter(exchange, chain { }).block()
assertTrue(
exchange.response.statusCode == HttpStatus.UNAUTHORIZED,
"expected 401, got ${exchange.response.statusCode}",
)
}
private fun chain(onCall: () -> Unit): GatewayFilterChain =
GatewayFilterChain { _: ServerWebExchange ->
onCall()
Mono.empty()
}
}