0

我正在使用 Bcrypt 对密码进行哈希处理,并根据 Spring 文档使用 Random。我正在使用 Spring Security 3.2.5.RELEASE。

我有以下 OAuth2 安全配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security/oauth2
http://www.springframework.org/schema/security/spring-security-oauth2.xsd">

    <beans:bean id="userService" class="com.nando.api.service.DefaultUserService" />
    <beans:bean id="webServiceClientService"
        class="com.nando.api.service.DefaultWebServiceClientService" />
    <beans:bean id="clientDetailsUserService"
        class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <beans:constructor-arg ref="webServiceClientService" />
    </beans:bean>

    <beans:bean id="sessionRegistry"
        class="org.springframework.security.core.session.SessionRegistryImpl" />

    <beans:bean id="webSecurityExpressionHandler"
        class="org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler" />
    <beans:bean id="methodSecurityExpressionHandler"
        class="org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler" />

    <beans:bean id="passwordEncoder"
        class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

    <beans:bean id="tokenStore"
        class="com.nando.api.service.DefaultAccessTokenService" />

    <beans:bean id="oauthRequestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
        <!-- TODO arguments here -->
        <beans:constructor-arg name="clientDetailsService" ref="webServiceClientService" />
        <!-- <beans:property name="securityContextAccessor" ref="oauthRequestFactory" /> -->
    </beans:bean>

    <beans:bean id="userApprovalHandler"
        class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
        <beans:property name="tokenStore" ref="tokenStore" />
        <!-- TODO here -->
        <beans:property name="requestFactory" ref="oauthRequestFactory" />
    </beans:bean>

    <beans:bean id="oauthAccessDeniedHandler"
        class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />

    <beans:bean id="oauthAuthenticationEntryPoint"
        class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" />

    <authentication-manager>
        <authentication-provider user-service-ref="userService">
            <password-encoder ref="passwordEncoder" />
        </authentication-provider>
    </authentication-manager>

    <authentication-manager id="oauthClientAuthenticationManager">
        <authentication-provider user-service-ref="clientDetailsUserService">
            <password-encoder ref="passwordEncoder" />
        </authentication-provider>
    </authentication-manager>

    <oauth2:authorization-server
        token-services-ref="tokenStore" client-details-service-ref="webServiceClientService"
        user-approval-page="oauth/authorize" error-page="oauth/error">
        <oauth2:authorization-code />
    </oauth2:authorization-server>

    <beans:bean id="nonceServices"
        class="com.nando.api.service.DefaultOAuthNonceService" />
    <beans:bean id="resourceServerFilter"
        class="com.nando.api.filters.OAuthSigningTokenAuthenticationFilter">
        <beans:property name="authenticationEntryPoint" ref="oauthAuthenticationEntryPoint" />
        <beans:property name="nonceServices" ref="nonceServices" />
        <beans:property name="tokenStore" ref="tokenStore" />
        <beans:property name="resourceId" value="nando" />
    </beans:bean>

    <global-method-security pre-post-annotations="enabled"
        order="0" proxy-target-class="true">
        <expression-handler ref="methodSecurityExpressionHandler" />
    </global-method-security>

    <http security="none" pattern="/resource/**" />
    <http security="none" pattern="/favicon.ico" />

    <http use-expressions="true" create-session="stateless"
        authentication-manager-ref="oauthClientAuthenticationManager"
        entry-point-ref="oauthAuthenticationEntryPoint" pattern="/oauth/token">
        <intercept-url pattern="/oauth/token" access="hasAuthority('OAUTH_CLIENT')" />
        <http-basic />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
        <expression-handler ref="webSecurityExpressionHandler" />
    </http>

    <http use-expressions="true" create-session="stateless"
        entry-point-ref="oauthAuthenticationEntryPoint" pattern="/services/**">
        <intercept-url pattern="/services/**"
            access="hasAuthority('USE_WEB_SERVICES')" />
        <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
        <expression-handler ref="webSecurityExpressionHandler" />
    </http>

    <http use-expressions="true">
        <intercept-url pattern="/session/list"
            access="hasAuthority('VIEW_USER_SESSIONS')" />
        <intercept-url pattern="/oauth/**"
            access="hasAuthority('USE_WEB_SERVICES')" />
        <intercept-url pattern="/login/**" access="permitAll()" />
        <intercept-url pattern="/login" access="permitAll()" />
        <intercept-url pattern="/logout" access="permitAll()" />
        <intercept-url pattern="/**" access="isFullyAuthenticated()" />
        <form-login default-target-url="/ticket/list" login-page="/login"
            login-processing-url="/login/submit" authentication-failure-url="/login?loginFailed"
            username-parameter="username" password-parameter="password" />
        <logout logout-url="/logout" logout-success-url="/login?loggedOut"
            delete-cookies="JSESSIONID" invalidate-session="true" />
        <session-management invalid-session-url="/login"
            session-fixation-protection="changeSessionId">
            <concurrency-control error-if-maximum-exceeded="true"
                max-sessions="1" session-registry-ref="sessionRegistry" />
        </session-management>
        <csrf />
        <expression-handler ref="webSecurityExpressionHandler" />
    </http>

</beans:beans>

问题是:

当它运行时,它会转到 org.springframework.security.authentication.dao.DaoAuthenticationProvider 和方法:

additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) 失败,因为盐为空。

它应该有一个随机盐。这就是我创建密码并存储在数据库中的方式。我已经验证了从数据库中检索到的密码并且是正确的。

UserPrincipal principal = new UserPrincipal();
        principal.setUserName(userName);
        UUID userID = UUIDs.timeBased();
        principal.setUserID(userID);

        String salt = BCrypt.gensalt(HASHING_ROUNDS, RANDOM);
        byte[] hash = BCrypt.hashpw(password, salt).getBytes();
        principal.setHashedPassword(Converters.byteArray2ByteBuffer(hash));

在哪里:

private static final SecureRandom RANDOM;

    static {
        try {
            RANDOM = SecureRandom.getInstanceStrong();
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException(e);
        }
    }
private static final int HASHING_ROUNDS = 10;

密码经过哈希处理并正确生成。它已正确保存在数据库中并正确检索。但是当它被检查时,提供的密码不会被散列,因此比较失败。

首先, this.saltSource 为空,因为我没有定义盐豆。我认为这是随机的,盐是旧的做法。

现在,在查看调试器时:

if (this.saltSource != null) {
            salt = this.saltSource.getSalt(userDetails);
        }

        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
        }

        String presentedPassword = authentication.getCredentials().toString();

        if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
            logger.debug("Authentication failed: password does not match stored value");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
        }

presentPassword 没有经过哈希处理,salt 为空。因此 (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentPassword, salt)) 测试检查并失败。

如果我使用的是随机盐,我怎样才能使它起作用?我错过了什么?

4

0 回答 0