Original address: http://my.oschina.net/huangyong/blog/287791
Catalogue [-]
- 1. User-Token-based identity authentication
- 2. Digital signature-based identity authentication
- 3. Encryption and decryption of SOAP messages
- 4. Summary
Through the previous article, I believe you have learned how to use CXF to develop SOAP-based WS. Perhaps you do not understand the underlying reason at present, the heart will inevitably have some doubts:
What is WSDL?
What is SOAP?
How can SOAP be made more secure?
I will strive to pass this article, in response to the above questions, let you get a satisfactory answer.
What are you waiting for? Let's start with the WSDL!
The full name of the WSDL is the Web Services Description Language (Web Service Description Language), which describes the specific content of WS.
Once you have successfully published a WS, you can view the WSDL-based document in a browser with an address, which is an XML-based document. A typical WSDL address is as follows:
http://localhost:8080/ws/soap/hello?wsdl
Note: The WSDL address must have a wsdl
parameter.
In the browser, you will see a standard XML document:
Where definitions
is the root node of the WSDL, which consists of two important attributes:
- The NAME:WS name, which defaults to "WS implementation class + Service", for example: Helloserviceimplservice
- Targetnamespace:ws the target namespace, which defaults to the address that the WS implementation class corresponds to after the package name is inverted, for example: http://soap_spring_cxf.ws.demo/
Tip: You can javax.jws.WebService
configure the above two attribute values in the annotations, but this configuration must be done on the WS implementation class, and the WS-Interface class simply needs to annotate a WebService annotation.
Under the root node of definitions, there are five types of child nodes, each of which are:
- Types: Describes the data types involved in WS
- PortType: Defines the WS interface name (Endpointinterface) and its operation name, as well as the input and output messages for each operation
- Message: Related messages are defined (for types and portType use)
- Binding: Provides a way to bind data to WS
- Service:ws name and its port name (PortName), and the corresponding WSDL address
It includes two important information:
- The port name of PORTNAME:WS, which defaults to "WS implementation class + port", for example: Helloserviceimplport
- The interface name of the ENDPOINTINTERFACE:WS, which defaults to the interface implemented by the WS implementation class, for example: HelloService
Tip: You can javax.jws.WebService
configure PortName and endpointinterface in annotations, as well as on the WS implementation class.
If WSDL is used to describe what WS is, then SOAP is used to indicate what is in WS.
In fact, SOAP is an envelope (Envelope), which consists of two parts, one head (header) and two body (body). The data used for the transfer is placed in the Body, and some special attributes need to be placed in the Header (see below).
In general, the data that needs to be transferred is put into the Body, and the Header is nothing, and it looks like the entire SOAP message is:
As can be seen, the request Header of HTTP requests and the request Body, which is exactly the same as the structure of the SOAP message!
See here, you may have a lot of questions:
- WS should not be allowed to be called by anyone, so it is not safe, at least need to do an identity authentication it?
- Can the data in the SOAP Body be encrypted in order to prevent third-party malicious programs from monitoring the WS-call process?
- What exactly can be stored in the SOAP Header?
That's right! That's what we're going to talk about today--SOAP-based security controls.
There is a very strong solution in WS, called WS-Security
, it is only a specification, in the Java industry has a very authoritative implementation, called wss4j.
Here I will step by step to learn how to use Spring
+ CXF
+ to WSS4J
implement a secure and reliable WS-call framework.
In fact, you need to do two things:
- Authentication WS Request
- Encrypt SOAP messages
How do I authenticate to WS? The following solutions can be used:
1. User-Token-based identity authentication
Step One: Add CXF-provided ws-security Maven dependencies
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>${cxf.version}</version></dependency>
In fact, the bottom of the implementation or WSS4J,CXF just to do a package.
Step Two: Complete the server CXF related configuration
<?xml version= "1.0" encoding= "UTF-8"? ><beans xmlns= "Http://www.springframework.org/schema/beans" xmlns: Xsi= "Http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf= "Http://cxf.apache.org/core" xmlns:jaxws= "http://cxf . Apache.org/jaxws "xsi:schemalocation=" Http://www.springframework.org/schema/beans http://www.springframework. Org/schema/beans/spring-beans-4.0.xsd Http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd Http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd "> <bean id=" wss4jininterceptor "class=" Org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor "> <constructor-arg> <map> <!--user authentication (Clear text password)--<entry key= "action" value= "UsernameToken"/> <entry ke Y= "Passwordtype" value= "Passwordtext"/> <entry key= "passwordcallbackref" value-ref= "ServerPasswordCal Lback "/> </map> </constructor-arg> </bean> <jaxws:endpoint id= "HelloService" implementor= "#helloServic Eimpl "address="/soap/hello "> <jaxws:inInterceptors> <ref bean=" Wss4jininterceptor "/> </jaxws:inInterceptors> </jaxws:endpoint> <cxf:bus> <cxf:features> < ;cxf:logging/> </cxf:features> </cxf:bus></beans>
First, a wss4j-based interceptor (Wss4jininterceptor) is defined, which is then configured on the HelloService and finally uses the bus feature provided by CXF, which simply configures a logging feature on the bus to Monitor the log of each WS request and response.
Note: This wss4jininterceptor is a ininterceptor that intercepts the incoming message and also outinterceptor, which means that the output message is intercepted. Since these are server-side configurations, we only need to configure Ininterceptor, and for clients we can configure Outinterceptor (see below).
It is necessary to make a description of the constructor parameters for Wss4jininterceptor in the above configuration.
- Action = UsernameToken: Indicates authentication using a user name Token-based approach
- Passwordtype = Passwordtext: Indicates that the password appears in clear text
- Passwordcallbackref = Serverpasswordcallback: Requires a callback processor (CallbackHandler) for password authentication
The following are the specific implementations of Serverpasswordcallback:
Package Demo.ws.soap_spring_cxf_wss4j;import Java.io.ioexception;import Java.util.hashmap;import java.util.Map; Import Javax.security.auth.callback.callback;import Javax.security.auth.callback.callbackhandler;import Javax.security.auth.callback.unsupportedcallbackexception;import Org.apache.wss4j.common.ext.WSPasswordCallback ; Import org.springframework.stereotype.Component; @Componentpublic class Serverpasswordcallback implements CallbackHandler {private static final map<string, string> usermap = new hashmap<string, string> (); static {Usermap.put ("client", "Clientpass"); Usermap.put ("Server", "Serverpass"); } @Override public void handle (callback[] callbacks) throws IOException, unsupportedcallbackexception {Wspas Swordcallback callback = (wspasswordcallback) callbacks[0]; String clientusername = Callback.getidentifier (); String Serverpassword = Usermap.get (clientusername); if (Serverpassword! = null) {calLback.setpassword (Serverpassword); } }}
Visible, it implements the javax.security.auth.callback.CallbackHandler
interface, which is the callback processor interface provided by the JDK for secure authentication. Two users are provided in the code, namely client and server, and the username and password are stored in UserMap. This requires the transformation provided by the JDK javax.security.auth.callback.Callback
to wss4j org.apache.wss4j.common.ext.WSPasswordCallback
, which validates the client's password in the handle method, and ultimately requires the password to be placed in the callback object.
Step Three: Complete client CXF related configuration
<?xml version= "1.0" encoding= "UTF-8"? ><beans xmlns= "Http://www.springframework.org/schema/beans" xmlns: Xsi= "Http://www.w3.org/2001/XMLSchema-instance" xmlns:context= "Http://www.springframework.org/schema/context" Xmlns:jaxws= "Http://cxf.apache.org/jaxws" xsi:schemalocation= "Http://www.springframework.org/schema/beans http ://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context htt P://www.springframework.org/schema/context/spring-context-4.0.xsd Http://cxf.apache.org/jaxws Http://cxf.apach E.org/schemas/jaxws.xsd "> <context:component-scan base-package=" demo.ws "/> <bean id=" Wss4joutinterceptor "class=" Org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor "> <constructor-arg> <map> <!--user authentication (Clear text password)--<entry key= "action" value= "UsernameToken"/> ; <entry key= "user" value= "clIent "/> <entry key=" Passwordtype "value=" Passwordtext "/> <entry key=" passwordcal Lbackref "value-ref=" Clientpasswordcallback "/> </map> </constructor-arg> </bean> <jaxws:client id= "HelloService" serviceclass= "demo.ws.soap_spring_cxf_wss4j. HelloService "address=" Http://localhost:8080/ws/soap/hello "> <jaxws:outInterceptors> <ref bean= "Wss4joutinterceptor"/> </jaxws:outInterceptors> </jaxws:client></beans> ;
Note: Wss4joutinterceptor is used here, which is a outinterceptor that enables the client to intercept the output messages.
Wss4joutinterceptor configuration is basically the same as Wss4jininterceptor, where you need to provide the client's username (user = client), You will also need to provide a client password callback processor (Passwordcallbackref = Clientpasswordcallback) with the following code:
package demo.ws.soap_spring_cxf_wss4j;import java.io.IOException;import javax.security.auth.callback.Callback;import javax.security.auth.callback.CallbackHandler;import javax.security.auth.callback.UnsupportedCallbackException;import org.apache.wss4j.common.ext.WSPasswordCallback;import org.springframework.stereotype.Component;@Componentpublic class ClientPasswordCallback implements CallbackHandler { @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback callback = (WSPasswordCallback) callbacks[0]; callback.setPassword("clientpass"); }}
In Clientpasswordcallback no more than setting the password of the client user, nothing else is done. The client password can only be provided by a callback handler, not in Spring.
Fourth step: Calling WS and observing the console log
Deploy the app and start Tomcat and call WS again, and you'll see the following Payload in the Inbound Message in the Tomcat console:
It can be seen that the UsernameToken information is provided in the soap Header, but both Username and Password are plaintext and the soap Body is clear, which is clearly not the best solution.
If you change Passwordtype from Passwordtext to Passworddigest (both the server and the client need to make the same changes), you will see an encrypted password:
In addition to this authentication based on user name and password, there is a more secure authentication method called "Digital signature".
2. Digital signature-based identity authentication
Digital signatures are literally understood as a form of digital-based signatures. That is, when a client sends a SOAP message, it needs to be "signed" to prove its identity, and when the server receives the SOAP message, it needs to verify its signature ("check").
Both the client and the server have their own keystore, which holds the key pair, and the key pair is actually made up of the public key and the private key. When a client sends a SOAP message, it needs to sign with its own private key, and when the client receives the SOAP message, it needs to be checked with the public key provided by the client.
Because there is a request for the corresponding, so the client and the server side of the message call is actually bidirectional, that is, the client and server side of the keystore stored in the information is this:
- Client KeyStore: The client's private key (for signing), the public key of the server (for verification)
- Server-side KeyStore: Private key for the server (for signing), client's public key (for verification)
Remember a word: Use your private key to sign, use the other's public key for verification.
Visible Build KeyStore is the first thing we need to do.
First step: Generate KeyStore
Now you need to create a batch file named Keystore.bat with the following contents:
<!-- lang: shell -->@echo offkeytool -genkeypair -alias server -keyalg RSA -dname "cn=server" -keypass serverpass -keystore server_store.jks -storepass storepasskeytool -exportcert -alias server -file server_key.rsa -keystore server_store.jks -storepass storepasskeytool -importcert -alias server -file server_key.rsa -keystore client_store.jks -storepass storepass -nopromptdel server_key.rsakeytool -genkeypair -alias client -dname "cn=client" -keyalg RSA -keypass clientpass -keystore client_store.jks -storepass storepasskeytool -exportcert -alias client -file client_key.rsa -keystore client_store.jks -storepass storepasskeytool -importcert -alias client -file client_key.rsa -keystore server_store.jks -storepass storepass -nopromptdel client_key.rsa
In these commands, the command-line tool provided by the JDK is used, and keytool
you can click the following link for the command:
Http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html
Run the batch program, generate two files: Server_store.jks and Client_store.jks, then put Server_store.jks into the server classpath, put Client_store.jks into the client's Under the classpath. If you are running on this computer, then this machine is both a client and a server.
Step Two: Complete the server CXF related configuration
...<bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <!-- 验签(使用对方的公钥) --> <entry key="action" value="Signature"/> <entry key="signaturePropFile" value="server.properties"/> </map> </constructor-arg></bean>...
Where action is signature,server.properties content is as follows:
org.apache.ws.security.crypto.provider=org.apache.wss4j.common.crypto.Merlinorg.apache.ws.security.crypto.merlin.file=server_store.jksorg.apache.ws.security.crypto.merlin.keystore.type=jksorg.apache.ws.security.crypto.merlin.keystore.password=storepass
Step Three: Complete client CXF related configuration
...<bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <!-- 签名(使用自己的私钥) --> <entry key="action" value="Signature"/> <entry key="signaturePropFile" value="client.properties"/> <entry key="signatureUser" value="client"/> <entry key="passwordCallbackRef" value-ref="clientPasswordCallback"/> </map> </constructor-arg></bean>...
Where action is signature,client.properties content is as follows:
org.apache.ws.security.crypto.provider=org.apache.wss4j.common.crypto.Merlinorg.apache.ws.security.crypto.merlin.file=client_store.jksorg.apache.ws.security.crypto.merlin.keystore.type=jksorg.apache.ws.security.crypto.merlin.keystore.password=storepass
In addition, the client also needs to provide a signed user (Signatureuser) with a password callback processor (PASSWORDCALLBACKREF).
Fourth step: Calling WS and observing the console log
As can be seen, digital signatures are indeed a more secure way of authenticating, but the data in the SOAP Body cannot be encrypted and still "world".
How exactly can you encrypt and decrypt the data in a SOAP message?
3. Encryption and decryption of SOAP messages
In addition to providing signature and verification (Signature) features, WSS4J also provides encryption and decryption (ENCRYPT) functionality, which you only need to modify in the configuration of the server and client.
Service side:
...<bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <!-- 验签 与 解密 --> <entry key="action" value="Signature Encrypt"/> <!-- 验签(使用对方的公钥) --> <entry key="signaturePropFile" value="server.properties"/> <!-- 解密(使用自己的私钥) --> <entry key="decryptionPropFile" value="server.properties"/> <entry key="passwordCallbackRef" value-ref="serverPasswordCallback"/> </map> </constructor-arg></bean>...
Client:
...<bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <!-- 签名 与 加密 --> <entry key="action" value="Signature Encrypt"/> <!-- 签名(使用自己的私钥) --> <entry key="signaturePropFile" value="client.properties"/> <entry key="signatureUser" value="client"/> <entry key="passwordCallbackRef" value-ref="clientPasswordCallback"/> <!-- 加密(使用对方的公钥) --> <entry key="encryptionPropFile" value="client.properties"/> <entry key="encryptionUser" value="server"/> </map> </constructor-arg></bean>...
Visible, the client sends a SOAP message when it is signed (using its own private key) and encrypted (using the other's public key), the server receives a SOAP message when it checks (using the other's public key) and decrypts (using its own private key).
Now you see that the SOAP message should look like this:
It can be seen that the SOAP request is not only signed, but also encrypted, so that the communication is more secure and reliable.
However, there is still a problem, although the SOAP request is already safe, but the SOAP response does not have any security control, look at the following SOAP response:
How can I sign and encrypt a SOAP response? I believe you must have a way to do it, you might as well try it yourself!
4. Summary
The content of this article is a bit more, it really needs a little summary:
- WSDL is used to describe the specific content of WS.
- SOAP is used to encapsulate the WS request and response
- WS can be authenticated using "User token" (supports plaintext password and ciphertext password)
- WS can be authenticated using digital signatures
- SOAP messages can be encrypted and decrypted
This is also the matter with regard to "SOAP security control", but the "WS thing" is not over because RESTful Web Services is waiting for you. How do I publish a REST service? How do I safely control the REST service? We'll see you next time!
SOAP and its security control--Reprint