2.CXF安全訪問之Http Basic Auth(一)
阿新 • • 發佈:2019-02-20
CXF涉及安全方面主要有三個途徑:
- 最簡單的方式是使用Http Basic Auth,就是WSS4J的UsernameToken實現方式,優點是簡單易用,缺點是每次都會在MESSAGE裡面傳密碼,安全性低。
- Transport level(傳輸層內)的實現Https。CXF samples裡面有一個例子wsdl_first_https, 很詳細的講了怎麼使用。
- 對MESSAGE進行加密和簽名(encryption and signing),請參考官方教程。
先來看第一種實現方式,按照上一篇教程中的內容先配置好,然後根據下面順序進行修改
1. 修改服務端spring配置檔案
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <bean id="serverPasswordCallback" class="com.demo.cxf.callbacks.ServerPasswordCallback"></bean> <bean id="serverWSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken Timestamp" /> <!-- MD5加密明文密碼 --> <entry key="passwordType" value="PasswordDigest" /> <entry key="passwordCallbackRef"> <ref bean="serverPasswordCallback" /> </entry> </map> </constructor-arg> </bean> <jaxws:endpoint id="helloWorld" implementor="com.demo.cxf.helloword.impl.HelloWordImpl" address="/HelloWorld"> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" /> <ref bean="serverWSS4JInInterceptor"/> </jaxws:inInterceptors> <jaxws:outInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> </jaxws:outInterceptors> <jaxws:properties> <entry key="mtom-enabled" value="true" /> </jaxws:properties> </jaxws:endpoint> </beans>
2. 建立服務端CallbackHandler
package com.demo.cxf.callbacks; 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.ws.security.WSPasswordCallback; public class ServerPasswordCallback implements CallbackHandler { Map<String, String> user = new HashMap<String, String>(); { user.put("admin", "123"); user.put("su", "123"); } @Override public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException { WSPasswordCallback wpc = (WSPasswordCallback) callbacks[0]; if (!user.containsKey(wpc.getIdentifier())) { throw new SecurityException("No Permission!"); } /* * 此處特別注意:: * WSPasswordCallback 的passwordType屬性和password 屬性都為null, * 你只能獲得使用者名稱(identifier), * 一般這裡的邏輯是使用這個使用者名稱到資料庫中查詢其密碼, * 然後再設定到password 屬性,WSS4J 會自動比較客戶端傳來的值和你設定的這個值。 * 你可能會問為什麼這裡CXF 不把客戶端提交的密碼傳入讓我們在ServerPasswordCallbackHandler 中比較呢? * 這是因為客戶端提交過來的密碼在SOAP 訊息中已經被加密為MD5 的字串, * 如果我們要在回撥方法中作比較,那麼第一步要做的就是把服務端準備好的密碼加密為MD5 字串, * 由於MD5 演算法引數不同結果也會有差別,另外,這樣的工作CXF 替我們完成不是更簡單嗎? */ wpc.setPassword(user.get(wpc.getIdentifier()));//如果包含使用者名稱,就設定該使用者名稱正確密碼,由CXF驗證密碼 String username = wpc.getIdentifier(); String password = wpc.getPassword(); System.out.println("userName:" + username + " password:" + password); } }
3. 修改客戶端spring配置檔案
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jaxws="http://cxf.apache.org/jaxws" 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 http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <bean id="clientOutPasswordCallback" class="cxf.callbacks.ClientOutPasswordCallback"></bean> <bean id="clientWSS4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken Timestamp" /> <!-- MD5加密明文密碼 --> <entry key="passwordType" value="PasswordDigest" /> <entry key="user" value="admin" /> <entry key="passwordCallbackRef"> <ref bean="clientOutPasswordCallback" /> </entry> </map> </constructor-arg> </bean> <jaxws:client id="helloClient" serviceClass="com.demo.cxf.helloword.HelloWord" address="http://localhost:8080/webservice/services/HelloWorld"> <jaxws:outInterceptors> <ref bean="clientWSS4JOutInterceptor"/> </jaxws:outInterceptors> </jaxws:client> </beans>
4. 建立客戶端CallbackHandler
package cxf.callbacks;
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.ws.security.WSPasswordCallback;
public class ClientOutPasswordCallback implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
if (callbacks != null && callbacks.length > 0) {
Callback callback = callbacks[0];
// 設定使用者密碼,供服務端驗證,可以從配置檔案或者資料庫裡面讀取
WSPasswordCallback wsc = (WSPasswordCallback) callback;
wsc.setPassword("123");
}
}
}
5. 呼叫程式碼
ApplicationContext context = new ClassPathXmlApplicationContext(
"cxf/cxf-client.xml");
HelloWord helloWord = (HelloWord) context.getBean("helloClient");
System.out.println(helloWord.sayHello("Bruce"));