springboot webService----wss4j+cxf实现WS-Security(基于UsernameToken)

qhl100 发布于 2019/03/04 16:56
阅读 3K+
收藏 1

开源之夏第三届火热来袭,高校学生参与赢万元奖金!>>>

最近在搞webservice+cxf 安全问题

遇到了这样的问题,就是服务端和客户端使用WSHandlerConstants.USERNAME_TOKEN和WSConstants.PW_TEXT 明文校验安全认证,客户端调服务端是没有问题,正常调用能返回值。

但是,如果客户端和服务端采用WSConstants.PW_DIGEST密码类型为加密时就会报错。

求解!!!!!!!!!!!!!!!!

 

springboot 服务端配置类

@Configuration
public class WebServiceConfig {
    @Autowired
    private Bus bus;
 
    @Autowired
    CommonService service;
    
    @Autowired
    AuthInterceptor authinterceptor;
    
    
    /*jax-ws*/
    @Bean
    public Endpoint endpoint() {
    	
    	
        EndpointImpl endpoint = new EndpointImpl(bus, service);
       // endpoint.getInInterceptors().add(authinterceptor);//添加校验拦截器
        endpoint.getInInterceptors().add(new LoggingInInterceptor());
       endpoint.getInInterceptors().add(new LoggingOutInterceptor());
        Map<String, Object> inProps = new HashMap<String, Object>();
        inProps.put(WSHandlerConstants.USER, "admin");
        inProps.put(WSHandlerConstants.ACTION,WSHandlerConstants.USERNAME_TOKEN);//设置加密类型
        inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST); //设置密码类型为加密 
        inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,  
        		ExampleServiceInterceptor.class.getName());  //密码回调函数
        endpoint.getInInterceptors().add( new SAAJInInterceptor());
        endpoint.getInInterceptors().add(new WSS4JInInterceptor(inProps));
        endpoint.publish("/CommonService");
        return endpoint;
    }
}

服务端密码回调函数

public class ExampleServiceInterceptor implements CallbackHandler{
 
	private Map<String, String> passwords = new HashMap<String, String>();
	
	public ExampleServiceInterceptor() {
		passwords.put("admin", "password");//此处的对应的是验证信息-用户名+密码,必须与客户端一致才可验证通过
	}
	public void handle(Callback[] callbacks) throws IOException,
			UnsupportedCallbackException {
		
		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
			String identifier = pc.getIdentifier();//用户名
			int usage = pc.getUsage();//验证方式
			if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN
				// username token pwd...
				// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同
				// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
				// Changes片段
				pc.setPassword(passwords.get(identifier));// ▲【这里非常重要】▲
				// ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:
				// The
				// security token could not be authenticated or
				// authorized异常,服务端会认为客户端为非法调用
			}else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
				// set the password for client's keystore.keyPassword
				// ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同;
				// 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
				// Changes片段
				pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
				// ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The
				// security token could not be authenticated or
				// authorized异常,服务端会认为客户端为非法调用
			}
		}
	}
 
}

客户端调用类

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;

public class webserviceclient {

    //动态调用
    public static void main(String[] args) throws Exception {
        JaxWsDynamicClientFactory dcflient=JaxWsDynamicClientFactory.newInstance();

        Client client=dcflient.createClient("http://localhost:8080/services/CommonService?wsdl");
        //client.getOutInterceptors().add(new LoginInterceptor("root","admin"));
        
        Map<String, Object> outProps = new HashMap<String, Object>();
		outProps.put(WSHandlerConstants.ACTION,
				WSHandlerConstants.USERNAME_TOKEN);
		outProps.put(WSHandlerConstants.USER, "admin");
		outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
		// 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器
		outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
				ExampleServiceClientInterceptor.class.getName());
		ArrayList list = new ArrayList();
		// 添加cxf安全验证拦截器,必须
		list.add(new SAAJOutInterceptor());
		list.add(new WSS4JOutInterceptor(outProps));
		client.getOutInterceptors().addAll(list);
		 QName name=new QName("http://www.webservice.xxx.com","HelloWorld");  
         Object[] objects=client.invoke(name,"88888");
        System.out.println("*******"+objects[0].toString());

    }

客户端回调函数


 
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;
 
/** 
 * @author 
 * 类说明 
 */
public class ExampleServiceClientInterceptor implements CallbackHandler{
 
	private Map<String, String> passwords = new HashMap<String, String>();
	
	public ExampleServiceClientInterceptor() {
		passwords.put("admin", "password");
	}
	public void handle(Callback[] callbacks) throws IOException,
			UnsupportedCallbackException {
		
		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
			String identifier = pc.getIdentifier();
			int usage = pc.getUsage();
			
			if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN
				System.out.println(passwords.containsKey(identifier)+"="+passwords.get(identifier)+"-"+identifier);
				pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
			}else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE
				pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲
			}
		}
	}
 
}

报错信息:

16:22:32.774 [main] WARN  o.a.c.p.PhaseInterceptorChain - [doLog,475] - Interceptor for {http://www.webservice.xxx.com}CommonService#{http://www.webservice.xxx.com}HelloWorld has thrown exception, unwinding now
org.apache.cxf.binding.soap.SoapFault: Error reading XMLStreamReader: Unexpected character '{' (code 123) in prolog; expected '<'
 at [row,col {unknown-source}]: [1,1]
	at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:292)
	at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:70)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:833)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1695)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1572)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1373)
	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:673)
	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:537)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:446)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:361)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:339)
	at webserviceclient.main(webserviceclient.java:37)
Caused by: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '{' (code 123) in prolog; expected '<'
 at [row,col {unknown-source}]: [1,1]
	at com.ctc.wstx.sr.StreamScanner.throwUnexpectedChar(StreamScanner.java:653)
	at com.ctc.wstx.sr.BasicStreamReader.nextFromProlog(BasicStreamReader.java:2133)
	at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1181)
	at com.ctc.wstx.sr.BasicStreamReader.nextTag(BasicStreamReader.java:1204)
	at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:173)
	... 16 common frames omitted
Exception in thread "main" org.apache.cxf.binding.soap.SoapFault: Error reading XMLStreamReader: Unexpected character '{' (code 123) in prolog; expected '<'
 at [row,col {unknown-source}]: [1,1]
	at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:292)
	at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:70)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:833)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1695)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1572)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1373)
	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:673)
	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:537)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:446)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:361)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:339)
	at webserviceclient.main(webserviceclient.java:37)
Caused by: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '{' (code 123) in prolog; expected '<'
 at [row,col {unknown-source}]: [1,1]
	at com.ctc.wstx.sr.StreamScanner.throwUnexpectedChar(StreamScanner.java:653)
	at com.ctc.wstx.sr.BasicStreamReader.nextFromProlog(BasicStreamReader.java:2133)
	at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1181)
	at com.ctc.wstx.sr.BasicStreamReader.nextTag(BasicStreamReader.java:1204)
	at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:173)
	... 16 more

 

加载中
0
qhl100
qhl100

好几天了,自己终于找到问题了,是ehcache-xxx.jar 和ehcache-core-xxx.jar冲突, 

解决方法:

    <!-- Shiro使用EhCache-core缓存框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
            
            <exclusions>
                <exclusion>
                      <groupId>net.sf.ehcache</groupId>
                    <artifactId>ehcache-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

 <!-- 使用EhCache缓存框架 -->

         <dependency>
             <groupId>org.apache.cxf</groupId>
              <artifactId>cxf-rt-ws-security</artifactId>
             <version>3.2.5</version>
         </dependency>

 

OSCHINA
登录后可查看更多优质内容
返回顶部
顶部
返回顶部
顶部