AuthenticationHandlerAutoConfiguration.kt

package io.github.lishangbu.avalon.oauth2.authorizationserver.autoconfiguration

import io.github.lishangbu.avalon.oauth2.authorizationserver.login.InMemoryLoginFailureTracker
import io.github.lishangbu.avalon.oauth2.authorizationserver.login.JdbcLoginFailureTracker
import io.github.lishangbu.avalon.oauth2.authorizationserver.login.LoginFailureTracker
import io.github.lishangbu.avalon.oauth2.authorizationserver.login.RedisLoginFailureTracker
import io.github.lishangbu.avalon.oauth2.authorizationserver.web.authentication.OAuth2AccessTokenApiResultResponseAuthenticationSuccessHandler
import io.github.lishangbu.avalon.oauth2.authorizationserver.web.authentication.OAuth2ErrorApiResultAuthenticationFailureHandler
import io.github.lishangbu.avalon.oauth2.common.log.AuthenticationLogRecorder
import io.github.lishangbu.avalon.oauth2.common.properties.LoginFailureTrackerStoreType
import io.github.lishangbu.avalon.oauth2.common.properties.Oauth2Properties
import org.springframework.beans.factory.ObjectProvider
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService
import org.springframework.transaction.PlatformTransactionManager
import tools.jackson.databind.json.JsonMapper

/**
 * 认证处理器自动配置
 *
 * 提供登录失败跟踪器以及统一的认证成功和失败处理器
 */
@AutoConfiguration
class AuthenticationHandlerAutoConfiguration {
    /** 创建登录失败跟踪器 */
    @Bean
    @ConditionalOnMissingBean
    fun loginFailureTracker(
        oauth2PropertiesProvider: ObjectProvider<Oauth2Properties>,
        stringRedisTemplateProvider: ObjectProvider<StringRedisTemplate>,
        jdbcTemplateProvider: ObjectProvider<JdbcTemplate>,
        transactionManagerProvider: ObjectProvider<PlatformTransactionManager>,
    ): LoginFailureTracker {
        val properties = oauth2PropertiesProvider.ifAvailable
        return when (properties?.loginFailureTrackerStoreType ?: LoginFailureTrackerStoreType.MEMORY) {
            LoginFailureTrackerStoreType.MEMORY -> {
                InMemoryLoginFailureTracker(properties)
            }

            LoginFailureTrackerStoreType.REDIS -> {
                RedisLoginFailureTracker(
                    properties,
                    stringRedisTemplateProvider.ifAvailable
                        ?: error("`oauth2.login-failure-tracker-store-type=REDIS` requires a `StringRedisTemplate` bean."),
                )
            }

            LoginFailureTrackerStoreType.JDBC -> {
                JdbcLoginFailureTracker(
                    properties,
                    jdbcTemplateProvider.ifAvailable
                        ?: error("`oauth2.login-failure-tracker-store-type=JDBC` requires a `JdbcTemplate` bean."),
                    transactionManagerProvider.ifAvailable
                        ?: error("`oauth2.login-failure-tracker-store-type=JDBC` requires a `PlatformTransactionManager` bean."),
                )
            }
        }
    }

    /** 创建访问令牌认证成功处理器 */
    @Bean
    @ConditionalOnMissingBean
    fun accessTokenResponseAuthenticationSuccessHandler(
        logRecorderProvider: ObjectProvider<AuthenticationLogRecorder>,
        authorizationServiceProvider: ObjectProvider<OAuth2AuthorizationService>,
        oauth2PropertiesProvider: ObjectProvider<Oauth2Properties>,
        jsonMapper: JsonMapper,
    ): OAuth2AccessTokenApiResultResponseAuthenticationSuccessHandler =
        OAuth2AccessTokenApiResultResponseAuthenticationSuccessHandler(
            logRecorderProvider.getIfAvailable { AuthenticationLogRecorder.noop() },
            authorizationServiceProvider.ifAvailable,
            oauth2PropertiesProvider.ifAvailable,
            jsonMapper,
        )

    /** 创建 OAuth2 认证失败处理器 */
    @Bean
    @ConditionalOnMissingBean
    fun oauth2ErrorApiResultAuthenticationFailureHandler(
        logRecorderProvider: ObjectProvider<AuthenticationLogRecorder>,
        oauth2PropertiesProvider: ObjectProvider<Oauth2Properties>,
        jsonMapper: JsonMapper,
    ): OAuth2ErrorApiResultAuthenticationFailureHandler =
        OAuth2ErrorApiResultAuthenticationFailureHandler(
            logRecorderProvider.getIfAvailable { AuthenticationLogRecorder.noop() },
            oauth2PropertiesProvider.ifAvailable,
            jsonMapper,
        )
}