我正在使用Bcrypt来散列密码并使用Random作为Spring文档。我正在使用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身份验证)失败,因为salt为空。
它应该有一个随机盐。这就是我创建密码和存储在数据库中的方式。我已经验证了从数据库中检索到的密码,这是正确的。
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为null,因为我没有定义salt bean。我认为这是随机的,盐是旧的做法。
现在,在查看调试器时:
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没有哈希,盐是空的。所以(!passwordEncoder.isPasswordValid(userDetails.getPassword(),presentPassword,salt))测试检查并失败。
如果我使用的是随机盐,我怎样才能使用它?我错过了什么?