You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 2.7.3:命令行传参密码哈希后认证失败求助

问题描述

需求是通过命令行传入明文密码,在Spring Boot应用中完成哈希处理后用于用户认证,但使用admin/admin登录时抛出BadCredentialsException。

操作步骤与配置

  1. 命令行启动参数:
mvn spring-boot:run -Dspring-boot.run.arguments="--user.password=admin"
  1. application.yaml安全配置片段:
security:
  user:
    name: admin
    password: ${user.password}
  1. 自定义密码编码器MyPasswordEncoder:
class MyPasswordEncoder : PasswordEncoder {

    private val logger = LoggerFactory.getLogger(javaClass)
    // 需确保以下常量已正确定义
    private val argon2PasswordEncoder = Argon2PasswordEncoder(SALT_LENGTH, HASH_LENGTH, PARALLELISM, MEMORY_USE, ITERATIONS)

    override fun encode(decryptedPassword: CharSequence?): String {
        val hashedPassword : String = argon2PasswordEncoder.encode(decryptedPassword)
        logger.debug("Password Encoding Successful!")
        return hashedPassword
    }

    override fun matches(rawPassword: CharSequence?, encodedPassword: String?): Boolean {
        return argon2PasswordEncoder.matches(rawPassword, encodedPassword)
    }
}
  1. 未使用的委托编码器类MyPasswordDelegation:
class MyPasswordDelegation {
    fun createDelegatingPasswordEncoder(): PasswordEncoder {
        val idForEncode = "myEncoder"
        val encoders: MutableMap<String, PasswordEncoder> = mutableMapOf()
        encoders[idForEncode] = NCaaSPasswordEncoder() // 此处疑似笔误,应为MyPasswordEncoder()
        return DelegatingPasswordEncoder(idForEncode, encoders)
    }
}
  1. 安全配置类SecurityConfig:
@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Value("\${spring.security.user.name}")
    private val userName: String? = null

    @Value("\${spring.security.user.password}")
    private val password: String? = null

    @Autowired
    lateinit var appAuthenticationEntryPoint: AppAuthenticationEntryPoint

    @Bean
    fun passwordEncoder(): MyPasswordEncoder {
        return MyPasswordEncoder()
    }

    @Bean
    @Throws(Exception::class)
    fun userDetailsService(): InMemoryUserDetailsManager? {
        val userDetails : UserDetails = User.withUsername(userName).password(passwordEncoder().encode(password)).roles("USER").build()
        return InMemoryUserDetailsManager(userDetails)
    }

    @Throws(Exception::class)
    @Bean
    fun filterChain(httpSecurity : HttpSecurity): SecurityFilterChain {
        httpSecurity.csrf().disable()

        // Allow only HTTPS Requests
        httpSecurity.requiresChannel {
                channel -> channel.anyRequest().requiresSecure()
        }.authorizeRequests {
                authorize -> authorize.anyRequest().fullyAuthenticated().and().httpBasic().and().exceptionHandling().authenticationEntryPoint(appAuthenticationEntryPoint)
        }

        return httpSecurity.build()
    }
}

错误日志

Failed to process authentication request
org.springframework.security.authentication.BadCredentialsException: Bad credentials
    at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:79)
    at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:147)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:172)
问题分析与解决

1. 过滤器链配置写法错误

原filterChain方法中,authorizeRequests的Lambda链式写法逻辑错误,导致httpBasic认证未被正确添加到过滤器链中,认证流程无法正常触发。

修复代码:

@Throws(Exception::class)
@Bean
fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain {
    httpSecurity.csrf().disable()
        // 仅允许HTTPS请求
        .requiresChannel { channel ->
            channel.anyRequest().requiresSecure()
        }
        // 配置权限规则
        .authorizeRequests { authorize ->
            authorize.anyRequest().fullyAuthenticated()
        }
        // 启用HTTP Basic认证
        .httpBasic()
        // 配置认证异常处理
        .and()
        .exceptionHandling()
        .authenticationEntryPoint(appAuthenticationEntryPoint)

    return httpSecurity.build()
}

2. 避免重复创建PasswordEncoder实例

原userDetailsService方法中直接调用passwordEncoder()方法,虽@Bean默认是单例,但在配置类代理失效的场景下可能创建新实例,导致编码与匹配使用的编码器实例不一致(虽参数相同,但仍存在未知风险)。

修复代码:
通过注入方式获取PasswordEncoder实例,而非直接调用@Bean方法:

@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Value("\${spring.security.user.name}")
    private val userName: String? = null

    @Value("\${spring.security.user.password}")
    private val password: String? = null

    @Autowired
    lateinit var appAuthenticationEntryPoint: AppAuthenticationEntryPoint

    @Autowired
    private lateinit var passwordEncoder: PasswordEncoder

    @Bean
    fun passwordEncoder(): MyPasswordEncoder {
        return MyPasswordEncoder()
    }

    @Bean
    @Throws(Exception::class)
    fun userDetailsService(): InMemoryUserDetailsManager? {
        val encodedPassword = passwordEncoder.encode(password)
        // 添加日志验证编码结果
        LoggerFactory.getLogger(javaClass).info("Encoded password for user {}: {}", userName, encodedPassword)
        val userDetails: UserDetails = User.withUsername(userName)
            .password(encodedPassword)
            .roles("USER")
            .build()
        return InMemoryUserDetailsManager(userDetails)
    }

    // 修复后的filterChain方法同上
}

3. 验证Argon2参数合法性

确保MyPasswordEncoder中的Argon2参数为合法值,示例配置如下:

class MyPasswordEncoder : PasswordEncoder {

    private val logger = LoggerFactory.getLogger(javaClass)
    private const val SALT_LENGTH = 16
    private const val HASH_LENGTH = 32
    private const val PARALLELISM = 4
    private const val MEMORY_USE = 65536 // 64MB
    private const val ITERATIONS = 3
    private val argon2PasswordEncoder = Argon2PasswordEncoder(SALT_LENGTH, HASH_LENGTH, PARALLELISM, MEMORY_USE, ITERATIONS)

    // encode和matches方法不变
}

4. 清理无用代码

MyPasswordDelegation类未被使用,且存在疑似笔误(NCaaSPasswordEncoder应为MyPasswordEncoder),可直接删除该类,避免混淆。

验证步骤
  1. 启动应用后,查看日志中输出的Encoded password for user admin: xxxx,确认是标准的Argon2格式字符串(如$argon2id$v=19$m=65536,t=3,p=4$xxxx$xxxx)。
  2. 使用admin/admin发起HTTP Basic认证请求,验证是否能正常通过认证。

内容的提问来源于stack exchange,提问作者hell_storm2004

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 23:01:03