在验证之前加密密码

时间:2014-07-29 16:06:58

标签: spring-security spring-integration spring-ws

我必须验证一条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" />

UserDetailsS​​ervice实现:

 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解析它。我想以一种安全的方式做到这一点。

1 个答案:

答案 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);

    }
}