【问题标题】:BCryptPasswordEncoder not working after upgrade to spring secrity 5升级到 Spring Security 5 后 BCryptPasswordEncoder 无法正常工作
【发布时间】:2019-10-11 17:39:26
【问题描述】:

升级后 - 春天:4.3.23.RELEASE -> 5.1.6.RELEASE - 弹簧安全:4.2.12.RELEASE -> 5.1.5.RELEASE 我的 BCryptPasswordEncoder 实现不再起作用了。

WARN [o.bcrypt.BCryptPasswordEncoder: 90] 编码密码看起来不像 BCrypt

我添加了一些日志来获取 rawPassword、encodedPassword

弹簧4: test1234,$2a$10$vcwRRfjPKrnXoGhDlGzJUe8ntNJEcWlHqc1l8QiDmwroB3WkJ96ma

spring5/5.1: 1a7emcO6sXmV,1a7emcO6sXmV

所以问题是该方法是用一些已经编码或散列的密码版本调用的?我在任何记录的地方都找不到这种行为。如何关闭它?当然,密码以某种神奇的方式匹配,但我没有得到不区分大小写的行为。

public class IgnoreCaseBcryptPasswordEncoder extends BCryptPasswordEncoder {

    Logger logger = LogManager.getLogger(getClass());

    @Override
    public String encode(CharSequence rawPassword) {
        return super.encode(rawPassword.toString().trim().toLowerCase());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        logger.trace(rawPassword + "," + encodedPassword);
        logger.trace(Arrays.toString(new Throwable().getStackTrace()));
        return super.matches(rawPassword.toString().toLowerCase(), encodedPassword);
    }
}

和 XML 配置:

    <bean id="passwordEncoder" class="de.k2interactive.qeep.security.oauth2.IgnoreCaseBcryptPasswordEncoder"/>

    <sec:authentication-manager id="userAuthentificationManager">
        <sec:authentication-provider user-service-ref="customUserDetailsService">
            <sec:password-encoder ref="passwordEncoder" />
        </sec:authentication-provider>
    </sec:authentication-manager>

    <oauth:authorization-server token-endpoint-url="/oauth/token" client-details-service-ref="clientDetails" token-services-ref="tokenServices">
        <oauth:authorization-code />
        <oauth:implicit />
        <oauth:refresh-token />
        <oauth:client-credentials />
        <oauth:password authentication-manager-ref="userAuthentificationManager"/>
        <oauth:custom-grant token-granter-ref="qeepFacebookLogin"/>
        <oauth:custom-grant token-granter-ref="qeepGoogleLogin"/>
        <oauth:custom-grant token-granter-ref="qeepAccountKitLogin"/>
    </oauth:authorization-server>

    <oauth:client-details-service id="clientDetails">
        <oauth:client client-id="trusted-ios-client" authorized-grant-types="password,facebook_access_token,accountkit,authorization_code,refresh_token,implicit"
                      secret="XXXcutXXX" authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT" /> 
        <oauth:client client-id="trusted-android-client" authorized-grant-types="password,facebook_access_token,google_access_token,accountkit,authorization_code,refresh_token,implicit"
                      secret="XXXcutXXX" authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT" />                      
    </oauth:client-details-service>

堆栈跟踪(Spring5):

[de.k2interactive.qeep.security.oauth2.IgnoreCaseBcryptPasswordEncoder.matches(IgnoreCaseBcryptPasswordEncoder.java:31), 
org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:90),
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166),
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175),
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:200),
org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:180),
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107),
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334),
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74),
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107),
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334),
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56),
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107),
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334),
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105),
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334),
org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:157),
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334),
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215),
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178),
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357),
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270),
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193),
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166),
org.springframework.web.filter.AbstractRequestLoggingFilter.doFilterInternal(AbstractRequestLoggingFilter.java:262),
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107),
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193),
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166),
org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:109),
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193),
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166),
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200),
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96),
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490),
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139),
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92),
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678),
org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:679),
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74),
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343),
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408),
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66),
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836),
org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2120),
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49),
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149),
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624),
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61),
java.lang.Thread.run(Thread.java:748)]

【问题讨论】:

  • 您确定在您的请求中发送纯密码吗?你能在你的问题中添加带有标题的请求吗?
  • 这个请求没什么特别的,我用邮递员做的:POST {{baseurl}}/api/oauth/token?grant_type=password&scope=trust body: username password headers: Content-Type: application/x-www-form-urlencoded Authorization:Basic XXXXXXXXXXXXX Accept-Language Qeep-Api-Version: 15 Qeep-Client-Build: 42 一切都很好,回滚到最新的 spring+security 4
  • 您没有说您使用的是 OAuth2。您必须显示您的授权服务器配置(带有已配置的客户端)。我想,您正在使用密码编码器作为您的用户密码,但您正在使用它作为您的客户端密码。
  • 抱歉,这是真的,我正在使用最新的 spring-security-oauth2:2.3.6.RELASE。 oauth-token 和注册 api 调用由客户端凭据和所有其他使用用户 oauth 令牌的请求保护。
  • 我在上面添加了授权服务器和客户端详细信息。

标签: spring spring-security


【解决方案1】:

我发现了问题所在: 如果您不使用 DelegatingPasswordEncoder,则 ID 为 passwordEncoder 的 passwordEncoder 不仅用于匹配用户密码,还用于客户端机密(无需明确引用,仅通过 autoconfig (beanid))。对于 oauth 编码的用户密码,一切都很好,但客户端密码不是 xml 中的 oauth 编码。 hack in match 方法证明了这一点:

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        if (encodedPassword.startsWith("$2")) { //bcrypt encoded for the users password
            return super.matches(rawPassword.toString().toLowerCase(), encodedPassword);
        } else {
            return rawPassword.equals(encodedPassword); //plain-text (noop encoder) for the client-secret
        }
    }

所以我看到了 2 个解决方案: 1.) oauth 编码客户端机密 2.) 使用 DelegatingPasswordEncoder 和 oauth 作为默认值,并在 client-secret 中使用 {noop} 前缀。

【讨论】:

    猜你喜欢
    • 2013-09-25
    • 1970-01-01
    • 2016-11-07
    • 2020-03-18
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    • 2013-11-16
    • 2018-10-29
    相关资源
    最近更新 更多