DefaultAccessDeniedHandler.kt

package io.github.lishangbu.avalon.oauth2.common.web.access

import io.github.lishangbu.avalon.oauth2.common.result.SecurityErrorResultCode
import io.github.lishangbu.avalon.web.util.JsonResponseWriter
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.web.access.AccessDeniedHandler
import tools.jackson.databind.json.JsonMapper

/**
 * 访问拒绝响应处理器
 *
 * 将已认证但无权限的请求转换为统一的 JSON 错误响应
 */
class DefaultAccessDeniedHandler(
    jsonMapper: JsonMapper,
) : AccessDeniedHandler {
    /** JSON 映射器 */
    private val jsonMapper = jsonMapper

    /** 将访问拒绝异常写入 403 响应 */
    override fun handle(
        request: HttpServletRequest,
        response: HttpServletResponse,
        accessDeniedException: AccessDeniedException,
    ) {
        log.error(
            "AccessDeniedHandler invoked for request [{}], reason [{}]",
            request.requestURI,
            accessDeniedException.message,
        )

        JsonResponseWriter.writeFailedResponse(
            response,
            jsonMapper,
            HttpStatus.FORBIDDEN,
            SecurityErrorResultCode.FORBIDDEN,
            accessDeniedException.message.orEmpty(),
        )
    }

    companion object {
        /** 日志记录器 */
        private val log = LoggerFactory.getLogger(DefaultAccessDeniedHandler::class.java)
    }
}