我必须验证一条SOAP消息,该消息使用数据库中的加密密码以纯文本形式发送密码。我们对所有流量使用HTTPS,因此整个邮件都是加密的。
无论如何,我已经实现了以下SI配置,直到Spring尝试匹配密码。由于SOAP密码是纯文本并且db passowrd是加密的,因此我必须在Spring进行身份验证之前加密密码。我有服务方法来实现这一点。
<bean id="SOAPSecurityValidationCallbackHandler" class="org.springframework.ws.soap.security.wss4j.callback.SpringSecurityPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.ps.snt.ws.security.SNTUserDetailsService" />
UserDetailsService实现:
public class SNTUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("In security callback " + username);
boolean valid = true;
Integer zoneID = null;
StringBuffer errorMessages = new StringBuffer();
if(StringUtils.isEmpty(username)) {
// TODO wil lthis ever happen, does framework check first?
errorMessages.append("Username token cannot be empty");
valid = false;
} else {
Pattern pattern = Pattern.compile("^[\\w]+@+\\d\\d\\d\\d\\d");
Matcher matcher = pattern.matcher(username);
if(!matcher.matches()) {
valid = false;
errorMessages.append("Username token must be in the format 'user@zone'.");
}
else {
String[] parts = username.split("@");
username = parts[0];
zoneID = Integer.parseInt(parts[1]);
}
}
if(valid && username != null && zoneID != null) {
LoginService loginService = new LoginService();
ApplicationUserDO user = loginService.getUserByUsername(zoneID.toString(), username);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new GrantedAuthorityImpl("ROLE_USER"));
UserDetails userDetails = new User(user.getUsername(),
user.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
grantedAuths );
return userDetails;
} else {
System.out.println("Authetnication failed!");
throw new UsernameNotFoundException("Epic fail!");
}
}
}
用户详细信息服务执行其应有的操作 - 它从数据库返回具有密码的用户。
我尝试在SpringSecurityPasswordValidationCallbackHandler中覆盖handleUSernameTokenPrincipal以将密码设置为加密版本,但此方法永远不会被调用。覆盖handleUsername无用,因为我无权访问原始密码。
我想保留详细信息服务并使用此回调,但我需要在身份验证之前将主体中的密码设置为加密版本。
理想情况下,我希望在验证SOAP标头之后但在调用用户详细信息服务之前设置密码。不确定我是否可以使用另一个拦截器执行此操作,但我不想通过XML解析它。我想以一种安全的方式做到这一点。
答案 0 :(得分:1)
我在回调中找到了使用身份验证管理器的解决方案:
<bean id="SOAPSecurityValidationCallbackHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler">
<property name="authenticationManager" ref="CommonAuthenticationManager" />
</bean>
AuthenticationManager接口必须实现一种方法 - authenticate:
public class CommonAuthenticationManager implements AuthenticationManager {
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
String username = auth.getName();
String password = (String) auth.getCredentials();
... encrypt password
... validate against DB
// this will be available in the message flow to any channel
return new UsernamePasswordAuthenticationToken(auth.getName(),
auth.getCredentials(), grantedAuths);
}
}