当前位置: 首页 > 工具软件 > Spring LDAP > 使用案例 >

spring ldap_Spring 3,Spring Web Services 2和LDAP安全

史旺
2023-12-01

spring ldap

今年的开局很好,其中另一个“截止日期不会改变” /“跳过所有繁文tape节” / “狂野西部”类型的项目中,我必须弄清楚并使用相对而言实现一些功能。新的库和技术需要进行更改,Spring 3并不是新增功能,但是在Java 5,weblogic 10(.01)和Spring 2.5.6缓慢的企业环境中,这是相对的。

由于一般的时间限制,我在这篇文章中没有过多地介绍“ fluff”,只是使用多个XSD和LDAP安全性来创建和保护Spring 3和Spring WS 2 Web服务。

代码:

服务端点:ExampleServiceEndpoint
这是将在后面的配置中使用Web服务公开的类。

package javaitzen.spring.ws;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import javax.annotation.Resource;


@Endpoint
public class ExampleServiceEndpoint {

    private static final String NAMESPACE_URI = "http://www.briandupreez.net";

    /**
     * Autowire a POJO to handle the business logic
    @Resource(name = "businessComponent")
    private ComponentInterface businessComponent;
   */

    public ExampleServiceEndpoint() {
        System.out.println(">>  javaitzen.spring.ws.ExampleServiceEndpoint loaded.");
    }

    @PayloadRoot(localPart = "ProcessExample1Request", namespace = NAMESPACE_URI + "/example1")
    @ResponsePayload
    public Example1Response processExample1Request(@RequestPayload final Example1 request) {
        System.out.println(">> process example request1 ran.");
        return new Example1Response();
    }

    @PayloadRoot(localPart = "ProcessExample2Request", namespace = NAMESPACE_URI + "/example2")
    @ResponsePayload
    public Example2Response processExample2Request(@RequestPayload final Example2 request) {
        System.out.println(">> process example request2 ran.");
        return new Example2Response();
    }

}

代码:CustomValidationCallbackHandler

这是我编写的用于扩展AbstactCallbackHandler的自定义代码,它允许我们使用LDAP。
根据下面的CallbackHandler中的注释,最好有一个缓存管理器,例如Hazelcast或Ehcache来缓存经过身份验证的用户,这取决于安全/性能方面的考虑。

下面的Digest Validator可以直接在Sun库中使用,我只是想了解它是如何工作的。

package javaitzen.spring.ws;

import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
import com.sun.xml.wss.impl.misc.Base64;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Properties;


public class CustomValidationCallbackHandler extends AbstractCallbackHandler implements InitializingBean {

    private Properties users = new Properties();
    private AuthenticationManager ldapAuthenticationManager;

    @Override
    protected void handleInternal(final Callback callback) throws IOException, UnsupportedCallbackException {

        if (callback instanceof PasswordValidationCallback) {
            final PasswordValidationCallback passwordCallback = (PasswordValidationCallback) callback;
            if (passwordCallback.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
                final PasswordValidationCallback.DigestPasswordRequest digestPasswordRequest =
                        (PasswordValidationCallback.DigestPasswordRequest) passwordCallback.getRequest();
                final String password = users
                        .getProperty(digestPasswordRequest
                                .getUsername());
                digestPasswordRequest.setPassword(password);
                passwordCallback
                        .setValidator(new CustomDigestPasswordValidator());

            }
            if (passwordCallback.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
                passwordCallback
                        .setValidator(new LDAPPlainTextPasswordValidator());


            }
        } else {
            throw new UnsupportedCallbackException(callback);
        }

    }

    /**
     * Digest Validator.
     * This code is directly from the sun class, I was just curious how it worked.
     */
    private class CustomDigestPasswordValidator implements PasswordValidationCallback.PasswordValidator {
        public boolean validate(final PasswordValidationCallback.Request request) throws PasswordValidationCallback.PasswordValidationException {

            final PasswordValidationCallback.DigestPasswordRequest req = (PasswordValidationCallback.DigestPasswordRequest) request;
            final String passwd = req.getPassword();
            final String nonce = req.getNonce();
            final String created = req.getCreated();
            final String passwordDigest = req.getDigest();
            final String username = req.getUsername();

            if (null == passwd)
                return false;
            byte[] decodedNonce = null;
            if (null != nonce) {
                try {
                    decodedNonce = Base64.decode(nonce);
                } catch (final Base64DecodingException bde) {
                    throw new PasswordValidationCallback.PasswordValidationException(bde);
                }
            }
            String utf8String = "";
            if (created != null) {
                utf8String += created;
            }
            utf8String += passwd;
            final byte[] utf8Bytes;
            try {
                utf8Bytes = utf8String.getBytes("utf-8");
            } catch (final UnsupportedEncodingException uee) {
                throw new PasswordValidationCallback.PasswordValidationException(uee);
            }

            final byte[] bytesToHash;
            if (decodedNonce != null) {
                bytesToHash = new byte[utf8Bytes.length + decodedNonce.length];
                for (int i = 0; i < decodedNonce.length; i++)
                    bytesToHash[i] = decodedNonce[i];
                for (int i = decodedNonce.length;
                     i < utf8Bytes.length + decodedNonce.length;
                     i++)
                    bytesToHash[i] = utf8Bytes[i - decodedNonce.length];
            } else {
                bytesToHash = utf8Bytes;
            }
            final byte[] hash;
            try {
                final MessageDigest sha = MessageDigest.getInstance("SHA-1");
                hash = sha.digest(bytesToHash);
            } catch (final Exception e) {
                throw new PasswordValidationCallback.PasswordValidationException(
                        "Password Digest could not be created" + e);
            }
            return (passwordDigest.equals(Base64.encode(hash)));
        }

    }


    /**
     * LDAP Plain Text validator.
     */
    private class LDAPPlainTextPasswordValidator implements
            PasswordValidationCallback.PasswordValidator {

        /**
         * Validate the callback against the injected LDAP server.
         * Probably a good idea to have a cache manager - ehcache /  hazelcast injected to cache authenticated users.
         *
         * @param request the callback request
         * @return true if login successful
         * @throws PasswordValidationCallback.PasswordValidationException
         *
         */
        public boolean validate(final PasswordValidationCallback.Request request) throws PasswordValidationCallback.PasswordValidationException {
            final PasswordValidationCallback.PlainTextPasswordRequest plainTextPasswordRequest =
                    (PasswordValidationCallback.PlainTextPasswordRequest) request;
            final String username = plainTextPasswordRequest.getUsername();

            final Authentication authentication;
            final Authentication userPassAuth = new UsernamePasswordAuthenticationToken(username, plainTextPasswordRequest.getPassword());
            authentication = ldapAuthenticationManager.authenticate(userPassAuth);

            return authentication.isAuthenticated();

        }
    }

    /**
     * Assert users.
     *
     * @throws Exception error
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(users, "Users is required.");
        Assert.notNull(this.ldapAuthenticationManager, "A LDAP Authentication manager is required.");
    }


    /**
     * Sets the users to validate against. Property names are usernames, property values are passwords.
     *
     * @param users the users
     */
    public void setUsers(final Properties users) {
        this.users = users;
    }

    /**
     * The the authentication manager.
     *
     * @param ldapAuthenticationManager the provider
     */
    public void setLdapAuthenticationManager(final AuthenticationManager ldapAuthenticationManager) {
        this.ldapAuthenticationManager = ldapAuthenticationManager;
    }
}

服务配置:
端点,CallbackHandler和LDAP身份验证管理器的配置。
应用程序上下文–服务器端:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:sws="http://www.springframework.org/schema/web-services"
              xmlns:s="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/web-services
       http://www.springframework.org/schema/web-services/web-services-2.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.0.xsd">
 
 
    <sws:annotation-driven/>
    <context:component-scan base-package="javaitzen.spring.ws"/>
 
 
    <sws:dynamic-wsdl id="exampleService"
                      portTypeName="javaitzen.spring.ws.ExampleServiceEndpoint"
                      locationUri="/exampleService/"
                      targetNamespace="http://www.briandupreez.net/exampleService">
        <sws:xsd location="classpath:/xsd/Example1Request.xsd"/>
        <sws:xsd location="classpath:/xsd/Example1Response.xsd"/>
        <sws:xsd location="classpath:/xsd/Example2Request.xsd"/>
        <sws:xsd location="classpath:/xsd/Example2Response.xsd"/>
    </sws:dynamic-wsdl>
 
    <sws:interceptors>
        <bean id="validatingInterceptor"
              class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
            <property name="schema" value="classpath:/xsd/Example1Request.xsd"/>
            <property name="validateRequest" value="true"/>
            <property name="validateResponse" value="true"/>
        </bean>
        <bean id="loggingInterceptor"
              class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
           
        <bean class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
            <property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml"/>
            <property name="callbackHandlers">
                <list>
                    <ref bean="callbackHandler"/>
                </list>
            </property>
        </bean>
         
    </sws:interceptors>
 
 
 
    <bean id="callbackHandler" class="javaitzen.spring.ws.CustomValidationCallbackHandler">
        <property name="ldapAuthenticationManager" ref="authManager" />
    </bean>
 
    <s:authentication-manager alias="authManager">
        <s:ldap-authentication-provider
                user-search-filter="(uid={0})"
                user-search-base="ou=users"
                group-role-attribute="cn"
                role-prefix="ROLE_">
        </s:ldap-authentication-provider>
    </s:authentication-manager>
 
 
   <!-- Example... (inmemory apache ldap service) -->
    <s:ldap-server id="contextSource" root="o=example" ldif="classpath:example.ldif"/>
 
    <!--
    If you want to connect to a real LDAP server it would look more like:
    <s:ldap-server  id="contextSource" url="ldap://localhost:7001/o=example" manager-dn="uid=admin,ou=system" manager-password="secret">
    </s:ldap-server>-->
 
    <bean id="marshallingPayloadMethodProcessor"
          class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
        <constructor-arg ref="serviceMarshaller"/>
        <constructor-arg ref="serviceMarshaller"/>
    </bean>
 
    <bean id="defaultMethodEndpointAdapter"
          class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
        <property name="methodArgumentResolvers">
            <list>
                <ref bean="marshallingPayloadMethodProcessor"/>
            </list>
        </property>
        <property name="methodReturnValueHandlers">
            <list>
                <ref bean="marshallingPayloadMethodProcessor"/>
            </list>
        </property>
    </bean>
 
 
    <bean id="serviceMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
  <property name="classesToBeBound">
   <list>
    <value>javaitzen.spring.ws.Example1</value>
    <value>javaitzen.spring.ws.Example1Response</value>
    <value>javaitzen.spring.ws.Example2</value>
    <value>javaitzen.spring.ws.Example2Response</value>
   </list>
  </property>
        <property name="marshallerProperties">
            <map>
                <entry key="jaxb.formatted.output">
                     <value type="java.lang.Boolean">true</value>
                </entry>
            </map>
        </property>
 </bean>
 
</beans>

安全上下文–服务器端:

xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
    <xwss:RequireTimestamp maxClockSkew="60" timestampFreshnessLimit="300"/>
    <!-- Expect plain text tokens from the client -->
    <xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
    <xwss:Timestamp/>
    <!-- server side reply token -->
    <xwss:UsernameToken name="server" password="server1" digestPassword="false" useNonce="false"/>
</xwss:SecurityConfiguration>

Web XML:
这里没有什么特别的,只是Spring WS MessageDispatcherServlet。

spring-ws
        org.springframework.ws.transport.http.MessageDispatcherServlet
     
        transformWsdlLocationstrue
     1
   

   
        spring-ws
        /*

客户端配置:
要测试或使用该服务,您需要:
应用程序上下文–客户端测试:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
 
    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
 
    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory"/>
        <property name="marshaller" ref="serviceMarshaller"/>
        <property name="unmarshaller" ref="serviceMarshaller"/>
        <property name="defaultUri" value="http://localhost:7001/example/spring-ws/exampleService"/>
        <property name="interceptors">
            <list>
                <ref local="xwsSecurityInterceptor"/>
            </list>
        </property>
    </bean>
 
 
    <bean id="xwsSecurityInterceptor"
          class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
        <property name="policyConfiguration" value="testSecurityPolicy.xml"/>
        <property name="callbackHandlers">
            <list>
                <ref bean="callbackHandler"/>
            </list>
        </property>
    </bean>
 
    <!--  As a client the username and password generated by the server must match with the client! -->
    <!-- a simple callback handler to configure users and passwords with an in-memory Properties object. -->
    <bean id="callbackHandler"
          class="org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler">
        <property name="users">
            <props>
             <prop key="server">server1</prop>
            </props>
        </property>
    </bean>
 
 
    <bean id="serviceMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
  <property name="classesToBeBound">
   <list>
    <value>javaitzen.spring.ws.Example1</value>
    <value>javaitzen.spring.ws.Example1Response</value>
    <value>javaitzen.spring.ws.Example2</value>
    <value>javaitzen.spring.ws.Example2Response</value>
   </list>
  </property>
        <property name="marshallerProperties">
            <map>
                <entry key="jaxb.formatted.output">
                     <value type="java.lang.Boolean">true</value>
                </entry>
            </map>
        </property>
 </bean>

安全上下文–客户端:

<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
    <xwss:RequireTimestamp maxClockSkew="60" timestampFreshnessLimit="300"/>
    <!-- Expect a plain text reply from the server -->
    <xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
    <xwss:Timestamp/>
    <!-- Client sending to server -->
    <xwss:UsernameToken name="example" password="pass" digestPassword="false" useNonce="false"/>
</xwss:SecurityConfiguration>

与Java一样,在jar和版本方面可能会有一些细微差别,因此下面是我使用的pom的一部分。
依赖关系:

3.0.6.RELEASE
        2.0.2.RELEASE
    


    
        
        
            org.apache.directory.server
            apacheds-all
            1.5.5
            jar
            compile
        
        
            org.springframework.ws
            spring-ws-core
            ${spring-ws-version}
        
        
            org.springframework
            spring-webmvc
            ${spring-version}
        
        
            org.springframework
            spring-web
            ${spring-version}
        
        
            org.springframework
            spring-context
            ${spring-version}
        
        
            org.springframework
            spring-core
            ${spring-version}
        
        
            org.springframework
            spring-beans
            ${spring-version}
        
        
            org.springframework
            spring-oxm
            ${spring-version}
        
        
            org.springframework.ws
            spring-ws-security
            ${spring-ws-version}
        
        
            org.springframework.security
            spring-security-core
            ${spring-version}
        
        
            org.springframework.security
            spring-security-ldap
            ${spring-version}
        
        
            org.springframework.ldap
            spring-ldap-core
            1.3.0.RELEASE
        
        
            org.apache.ws.security
            wss4j
            1.5.12
        
        
            com.sun.xml.wss
            xws-security
            3.0
        
        
        
            org.apache.ws.commons.schema
            XmlSchema
            1.4.2
        
    

</project>

参考: Spring 3,Spring Web Services 2和LDAP安全。 来自我们的JCG合作伙伴   Zen博客中的Zen领域的 Brian Du Preez。


翻译自: https://www.javacodegeeks.com/2012/02/spring-3-spring-web-services-2-ldap.html

spring ldap

 类似资料: