Miscellaneous changes (#50)
* Add captcha feature toggle * Rename constant * Up spring + kotlin versions * Add spring security exception handler module * Fix broken object mappers * Add default spring web logger * Add captcha configuration * Enable captcha aspect by default
This commit is contained in:
parent
dfa3a75f4c
commit
5472e9dfdf
|
|
@ -2,16 +2,17 @@ package ru.touchin.captcha.aspects
|
|||
|
||||
import org.aspectj.lang.annotation.Aspect
|
||||
import org.aspectj.lang.annotation.Before
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.stereotype.Component
|
||||
import org.springframework.web.context.request.RequestContextHolder
|
||||
import org.springframework.web.context.request.ServletRequestAttributes
|
||||
import ru.touchin.captcha.annotations.Captcha
|
||||
import ru.touchin.captcha.exceptions.CaptchaResponseMissingException
|
||||
import ru.touchin.captcha.services.CaptchaService
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "captcha", name = ["enabled"], havingValue = "true", matchIfMissing = true)
|
||||
class CaptchaSiteVerifyAspect(private val captchaService: CaptchaService) {
|
||||
|
||||
@Throws(Throwable::class)
|
||||
|
|
@ -21,7 +22,7 @@ class CaptchaSiteVerifyAspect(private val captchaService: CaptchaService) {
|
|||
as? ServletRequestAttributes
|
||||
?: throw IllegalStateException("unable to get current request attributes")
|
||||
|
||||
val captchaResponse = currentRequestAttributes.request.getHeader(CAPTCHA_HEADER_NAME)
|
||||
val captchaResponse = currentRequestAttributes.request.getHeader(CAPTCHA_RESPONSE_HEADER_NAME)
|
||||
?: throw CaptchaResponseMissingException()
|
||||
|
||||
captchaService.verify(captchaResponse)
|
||||
|
|
@ -31,7 +32,7 @@ class CaptchaSiteVerifyAspect(private val captchaService: CaptchaService) {
|
|||
|
||||
companion object {
|
||||
|
||||
private const val CAPTCHA_HEADER_NAME = "X-Captcha-Response"
|
||||
private const val CAPTCHA_RESPONSE_HEADER_NAME = "X-Captcha-Response"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.touchin.captcha.configurations
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
@ComponentScan("ru.touchin.captcha")
|
||||
@ConfigurationPropertiesScan("ru.touchin.captcha")
|
||||
class CaptchaConfiguration
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
plugins {
|
||||
id("kotlin")
|
||||
id("kotlin-spring")
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":exception-handler-spring-web"))
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
@file:Suppress("unused")
|
||||
package ru.touchin.exception.handler.spring.security
|
||||
|
||||
import org.springframework.context.annotation.Import
|
||||
import ru.touchin.exception.handler.spring.security.configurations.SecurityExceptionHandlerConfiguration
|
||||
|
||||
@Import(value = [SecurityExceptionHandlerConfiguration::class])
|
||||
annotation class EnableSpringSecurityExceptionHandler
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.touchin.exception.handler.spring.security.configurations
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
@ComponentScan("ru.touchin.exception.handler.spring.security.resolvers")
|
||||
class SecurityExceptionHandlerConfiguration
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package ru.touchin.exception.handler.spring.security.resolvers
|
||||
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.stereotype.Component
|
||||
import ru.touchin.common.spring.Ordered
|
||||
import ru.touchin.common.spring.web.dto.DefaultApiError
|
||||
import ru.touchin.exception.handler.dto.ExceptionResolverResult
|
||||
import ru.touchin.exception.handler.spring.resolvers.ExceptionResolver
|
||||
|
||||
@Order(Ordered.LOW)
|
||||
@Component
|
||||
class AccessDeniedExceptionResolver : ExceptionResolver {
|
||||
|
||||
override fun invoke(exception: Exception): ExceptionResolverResult? {
|
||||
if (exception is AccessDeniedException) {
|
||||
return createAccessDeniedError(exception)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createAccessDeniedError(exception: Exception?): ExceptionResolverResult {
|
||||
return ExceptionResolverResult(
|
||||
apiError = DefaultApiError.createFailure(exception?.message),
|
||||
status = HttpStatus.FORBIDDEN,
|
||||
exception = exception,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.touchin.exception.handler
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import ru.touchin.exception.handler.spring.EnableSpringExceptionHandler
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableSpringExceptionHandler
|
||||
class TestApplication
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package ru.touchin.exception.handler.spring.advices
|
||||
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/errors")
|
||||
class ErrorController {
|
||||
|
||||
|
||||
@GetMapping("/unauthorized")
|
||||
fun runtimeError(): String {
|
||||
return "ok"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ActiveProfiles("test")
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
internal class ExceptionHandlerAdviceMvcTest {
|
||||
|
||||
@Autowired
|
||||
lateinit var mockMvc: MockMvc
|
||||
|
||||
@Test
|
||||
@DisplayName("Should be unauthorized")
|
||||
fun shouldGetUnauthorized() {
|
||||
mockMvc
|
||||
.perform(get("/api/errors/unauthorized"))
|
||||
.andExpect(status().isUnauthorized)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ dependencies {
|
|||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin")
|
||||
|
|
|
|||
|
|
@ -39,14 +39,6 @@ data class ExceptionResolverResult(
|
|||
)
|
||||
}
|
||||
|
||||
fun createAccessDeniedError(exception: Exception?): ExceptionResolverResult {
|
||||
return ExceptionResolverResult(
|
||||
apiError = DefaultApiError.createFailure(exception?.message),
|
||||
status = HttpStatus.FORBIDDEN,
|
||||
exception = exception,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package ru.touchin.exception.handler.spring.resolvers
|
||||
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.stereotype.Component
|
||||
import ru.touchin.common.exceptions.CommonNotFoundException
|
||||
import ru.touchin.common.spring.Ordered
|
||||
import ru.touchin.exception.handler.dto.ExceptionResolverResult
|
||||
|
||||
@Order(Ordered.LOW)
|
||||
@Component
|
||||
class AccessDeniedExceptionResolver : ExceptionResolver {
|
||||
|
||||
override fun invoke(exception: Exception): ExceptionResolverResult? {
|
||||
if (exception is AccessDeniedException) {
|
||||
return ExceptionResolverResult.createAccessDeniedError(exception)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
kotlin.code.style=official
|
||||
springBootVersion=2.4.1
|
||||
springBootVersion=2.5.3
|
||||
springDependencyManagementVersion=1.0.11.RELEASE
|
||||
kotlinVersion=1.4.32
|
||||
kotlinVersion=1.5.21
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.touchin.logger.spring.web.configurations
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
|
||||
@ComponentScan("ru.touchin.logger.spring.web")
|
||||
class SpringLoggerWebConfiguration
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package ru.touchin.logger.spring.web.webclients
|
||||
|
||||
import org.springframework.stereotype.Component
|
||||
import ru.touchin.common.spring.web.webclient.dto.RequestLogData
|
||||
import ru.touchin.common.spring.web.webclient.logger.WebClientLogger
|
||||
import ru.touchin.logger.dto.LogData
|
||||
import ru.touchin.logger.factory.LogBuilderFactory
|
||||
|
||||
@Component
|
||||
class DefaultWebClientLogger(
|
||||
private val logBuilderFactory: LogBuilderFactory<LogData>
|
||||
) : WebClientLogger {
|
||||
|
||||
override fun log(requestLogData: RequestLogData) {
|
||||
logBuilderFactory.create(this::class.java)
|
||||
.addTags(*requestLogData.logTags.toTypedArray())
|
||||
.setMethod(requestLogData.method.toString())
|
||||
.addData("uri" to requestLogData.uri)
|
||||
.also { builder ->
|
||||
requestLogData.requestBody?.also {
|
||||
builder.addData(
|
||||
"requestBody" to it,
|
||||
)
|
||||
}
|
||||
|
||||
requestLogData.responseBody?.also {
|
||||
builder.addData(
|
||||
"responseData" to it,
|
||||
)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
.log()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ dependencies {
|
|||
|
||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
implementation(project(":common-spring"))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package ru.touchin.logger.spring.serializers.resolvers
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.stereotype.Component
|
||||
import ru.touchin.common.spring.Ordered
|
||||
|
|
@ -13,6 +14,7 @@ import ru.touchin.logger.spring.serializers.resolvers.dto.ResolvedValue
|
|||
class ObjectLogValueResolverImpl : LogValueResolver<String> {
|
||||
|
||||
private val objectMapper = ObjectMapper()
|
||||
.registerModule(JavaTimeModule())
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
|
||||
override operator fun invoke(value: Any): ResolvedValue<String> {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package ru.touchin.logger.serializers.impl
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
|
|
@ -25,6 +26,7 @@ internal class LogValueFieldResolverImplTest {
|
|||
private lateinit var logValueFieldSerializer: LogValueFieldSerializer
|
||||
|
||||
private val objectMapper = ObjectMapper()
|
||||
.registerModule(JavaTimeModule())
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
|
||||
@Suppress("unused")
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ include("logger")
|
|||
include("logger-spring")
|
||||
include("logger-spring-web")
|
||||
include("exception-handler-spring-web")
|
||||
include("exception-handler-spring-security-web")
|
||||
include("exception-handler-logger-spring-web")
|
||||
include("validation-spring")
|
||||
include("version-spring-web")
|
||||
|
|
|
|||
Loading…
Reference in New Issue