spring ldap
由于一般的时间限制,我在这篇文章中没有过多地介绍“ 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")@ResponsePayloadpublic Example1Response processExample1Request(@RequestPayload final Example1 request) {System.out.println(">> process example request1 ran.");return new Example1Response();}@PayloadRoot(localPart = "ProcessExample2Request", namespace = NAMESPACE_URI + "/example2")@ResponsePayloadpublic 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;@Overrideprotected 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 implementsPasswordValidationCallback.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/web-serviceshttp://www.springframework.org/schema/web-services/web-services-2.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/securityhttp://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-provideruser-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-wsorg.springframework.ws.transport.http.MessageDispatcherServlettransformWsdlLocationstrue1spring-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.RELEASE2.0.2.RELEASEorg.apache.directory.serverapacheds-all1.5.5jarcompileorg.springframework.wsspring-ws-core${spring-ws-version}org.springframeworkspring-webmvc${spring-version}org.springframeworkspring-web${spring-version}org.springframeworkspring-context${spring-version}org.springframeworkspring-core${spring-version}org.springframeworkspring-beans${spring-version}org.springframeworkspring-oxm${spring-version}org.springframework.wsspring-ws-security${spring-ws-version}org.springframework.securityspring-security-core${spring-version}org.springframework.securityspring-security-ldap${spring-version}org.springframework.ldapspring-ldap-core1.3.0.RELEASEorg.apache.ws.securitywss4j1.5.12com.sun.xml.wssxws-security3.0org.apache.ws.commons.schemaXmlSchema1.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