我们创建的webService服务要有安全性,不能被人随便调用,要有用户认证,在传输过程中还要加密。
java WebService_cxf (1) 入门案例 中的创建的服务任何人都可以调用,这显然是不安全的,我们在入门案例基础上加入用户的认证。
服务器端:
<?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: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="cxfDemoImple" class="shzj.web.webService.service.imple.CxfDemoImple"/> <bean id="DemoServerPassWordCallback" class="shzj.web.webService.service.DemoServerPasswordCallback"/> <jaxws:endpoint id="hello" implementor="#cxfDemoImple" address="/helloWorld" > <jaxws:inInterceptors> <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" /> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="FHDClient" /> <entry key="passwordCallbackRef"> <ref bean="DemoServerPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </jaxws:inInterceptors> </jaxws:endpoint></beans >密码回调函数类:
package shzj.web.webService.service;import org.apache.ws.security.WSPasswordCallback;import javax.security.auth.callback.Callback;import javax.security.auth.callback.CallbackHandler;import javax.security.auth.callback.UnsupportedCallbackException;import java.io.IOException;public class DemoServerPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; String pw = pc.getPassword(); String idf = pc.getIdentifier(); //特别注意的是 这里的pw肯是null, 无论你password是否传值。cxf 2.4之后, //密码的比较是框架自动帮我们完成,因此不需要我们获取传递过来的密码,如果你 //一定要查看密码的话,可以通过new String(pc.getKey())获取。在该回调函数中 //我们只需要使用 idf 从数据库中查询出密码,使用pc.setPassword()方法将密码设 //置进去,框架获取的的密码后会进行比较,并通过抛出异常的方式提示验证出错。 System.out.println("密码是: " + pw); System.out.println("身份是: " + idf); //根据idf 我们从数据中查询出的密码 假如是 "abc" pc.setPassword("abc"); }}发布
客户端使用wsimport在客户端生成本地服务类。替换原来的。第一种实现方式:通过配置客户端来调用服务
spring-cxf-client.xml修改为:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core" 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/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <bean id="DemoClientPasswordCallback" class="shzj.web.webService.service.DemoClientPasswordCallback"/> <jaxws:client id="DemoClient" serviceClass="com.demo.client.CxfDemo" address="http://localhost:8080/shzj/webservice/helloWorld?wsdl"> <jaxws:outInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken"/> <entry key="passwordType" value="PasswordText"/> <entry key="user" value="FHDClient"/> <entry key="passwordCallbackRef"> <ref bean="DemoClientPasswordCallback"/> </entry> </map> </constructor-arg> </bean> </jaxws:outInterceptors> </jaxws:client></beans>密码回调函数类:package shzj.web.webService.service;import org.apache.ws.security.WSPasswordCallback;import javax.security.auth.callback.Callback;import javax.security.auth.callback.CallbackHandler;import javax.security.auth.callback.UnsupportedCallbackException;import java.io.IOException;public class DemoClientPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; String ident = "I"; String passwd = "2"; pc.setPassword(passwd); pc.setIdentifier(ident); }}测试:
@Test public void text2() { applicationContext factory = new ClassPathXmlApplicationContext("/spring-cxf-client.xml"); CxfDemo client = (CxfDemo)factory.getBean("DemoClient"); String response = client.helloWord("小米"); System.out.println("返回: " + response); }发送的数据为:<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"> <wsse:UsernameToken wsu:Id="UsernameToken-794D515C6E4EAC356E14864531467091"> <wsse:Username>I</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"> 2 </wsse:Password> </wsse:UsernameToken> </wsse:Security></SOAP-ENV:Header><soap:Body> <ns2:helloWord xmlns:ns2="http://service.webService.web.shzj/"> <userName>小米</userName> </ns2:helloWord></soap:Body></soap:Envelope>结果:出现异常,javax.xml.ws.soap.SOAPFaultException: The security token could not be authenticated or authorized很显然是认证出错了。应为密码出错。如果密码是abc,结果就是:返回: hello 小米!!!
cxf框架验证出错是通过抛出异常提示我们的,那么经常会抛出的异常有:
Fault from server : Missing wsse:Security in SOAP Header.Cause: This error is thrown by server if SOAP request does not contain a Web Service Security Header in SOAPHeader section of SOAPEnvelope.Resolution: Pass a valid Web Service Security Header, containing valid Username token, in SOAPHeader.(如果是SoapUI测试的话,也需要设置WSS-Password Type为PasswordText)Fault from server : Policy requires authentication token.Cause: This error is thrown by server when either Web Service Security Username Token in Security header is missing or is invalid.Resolution: Pass a valid Web Service Security Username Token in Security header.Fault from server : The security token could not be authenticated or authorizedCause: This error is thrown by server when username or(/and) password in Web Service Security Username Token is invalid.Resolution: Pass valid Oracle applications username and password in Web Service Security Username Token.Fault from server : User not authorized to execute serviceCause: This error is thrown by server when user given in Web Service Security Username Token is not authorized to execute the Web Service function being invoked in SOAP Request.Resolution: Create grant at user, role or global level from Integration Repository UI to authorize the the user to execute Web Service function. Clear cache from Functional Administrator Responsibility.Fault from server : Responsibility key is not validCause: This error is thrown by server when Responsibility Key passed in Responsibility Header in SOAHeader is invalid.Resolution: Server expects a valid Responsibility Key in Responsibility Header in SOAHeader.Use this query to find valid Responsibility Keys for a particular user:-Select resp.RESPONSIBILITY_KEY, grp.SECURITY_GROUP_KEY,APP.APPLICATION_SHORT_NAMEFrom FND_USER_RESP_GROUPS furg, FND_USER usr, fnd_responsibility_vlresp,FND_SECURITY_GROUPS grp,FND_APPLICATION APPwhere furg.user_id=usr.user_idand furg.RESPONSIBILITY_ID=resp.RESPONSIBILITY_IDand furg.SECURITY_GROUP_ID=grp.SECURITY_GROUP_IDand furg.RESPONSIBILITY_APPLICATION_ID=APP.APPLICATION_IDand usr.user_name= <username>Fault from server: Responsibility Application Short name is not valid.Cause: This error is thrown by server when Application Short name in RespApplication Header in SOAHeader is invalid.Resolution: Server expects a valid Application short name in RespApplication Header in SOAHeader. Use query given above to find a valid Application short name.Fault from server: Security Group Key is not valid.Cause: This error is thrown by server when Security Group Key in SecurityGroup Header in SOAHeader is invalid.Resolution: Server expects a valid Security Group Key in SecurityGroup Header in SOAHeader. Use query given above to find a valid Security Group Key.Fault from server: NLS Language is not valid.Cause: This error is thrown by server when NLS Language in NLSLanguage Header in SOAHeader is invalid.Resolution: Server expects a valid NLSLanguage value in NLSLanguage Header in SOAHeader. Use the following query to find a valid NLSLanguage:SELECT NLS_LANGUAGEFROM FND_LANGUAGESWHERE INSTALLED_FLAG in (‘B’,'I’);Fault from server: Service is not deployed.Cause: This error is thrown by server when invoked service is generated but not deployed.Resolution: Deploy this service from Integration Repository UI.Fault from server: Error occured while service was processing.Cause: This error is thrown by server when a internal error happens in Service Provider.Resolution: View SOAP Response via SOA Monitor UI. It has a error trace of the error happened in server. This should give a fair idea of what went wrong.第二种实现方式:通过JaxWsProxyFactoryBean代理类来设定服务处理类和服务地址,无须额外的客户端配置。
@Test public void text3() { // 以下和服务端配置类似,不对,应该说服务端和这里的安全验证配置一致 Map<String, Object> map = new HashMap<String, Object>(); map.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); map.put(WSHandlerConstants.USER, "admin"); map.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); // 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器 map.put(WSHandlerConstants.PW_CALLBACK_CLASS, DemoClientPasswordCallback.class.getName()); ArrayList list = new ArrayList(); // 添加cxf安全验证拦截器,必须 list.add(new SAAJOutInterceptor()); list.add(new WSS4JOutInterceptor(map)); JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); // WebServiceSample服务端接口实现类 factory.setServiceClass(CxfDemo.class); // 设置ws访问地址 factory.setAddress("http://localhost:8080/shzj/webservice/helloWorld?wsdl"); //注入拦截器,用于加密安全验证信息 factory.getOutInterceptors().addAll(list); CxfDemo service = (CxfDemo) factory.create(); String response = service.helloWord("小米"); System.out.println("返回: " + response); }
新闻热点
疑难解答