Cxf Webservice安全认证

(1) 2024-05-10 18:23

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Cxf Webservice安全认证,希望能够帮助你!!!。

在开发的时候发布webservice,为了安全通常需要安全验证,在CXF中的实现,在这里记录一下。

 

CXF是啥 我就不介绍了, 开发CXF+Spring的webservice服务:

 

 在这里发布一个简单的服务,比如发布的服务为SpingService

 

 写道
这里只是一个简单的接口,通过注解标注这是一个WebService接口:

import javax.jws.WebService;

@WebService

public interface SpringService {

String play(String info);

}

 

 

 

具体的实现类:

    写道

通过WebService注解中的endpointInterface指到刚才我定义的那个接口,发布出来的服务将会就是那个接口的样子:

import javax.jws.WebService;

@WebService(endpointInterface="cn.jd.ws.SpringService")

public class DotaSpringService implements SpringService{

public String play(String info) {

System.out.println("play called!");

return "Dota [ " + info + " ]";

}

}

 

 Spring中的配置:

  写道

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"


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://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

http://cxf.apache.org/jaxws


http://cxf.apache.org/schemas/jaxws.xsd

">

需要插入jaxws这个库

 

 

 写道

在spring的配置文件中导入cxf包下的文件:

<import resource="classpath:META-INF/cxf/cxf.xml"/>

<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>

<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>

 

 

在spring中发布服务:

 

 写道
把我们刚才实现的服务发布出来:

<jaxws:endpoint id="dotaService" implementor="cn.jd.ws.DotaSpringService" address="/DotaService">

<jaxws:inInterceptors>

<ref bean="soapAuth"/> 

</jaxws:inInterceptors>

</jaxws:endpoint>

参数implementor指定这个发布出来的WebService服务的实现类是哪个,address表示访问的地址,其中的拦截器就是我们需要将的那个安全验证的拦截器。稍后会介绍到。
<bean  id="soapAuth" class="cn.jd.ws.interceptor.SOAPAuthIntercepter"><property name="token" value="ssssdddd"></property></bean>
这里配置了一个令牌,当然为了安全最好是通过MD5等安全加密算法加密过的。为了简单直接搞了一个。

可能通过代码提示功能的我们都发现了还有一个发布服务的标签:

<jaxws:server id="" serviceClass="cn.jd.ws.DotaSpringService" address="/DataService">

<jaxws:inInterceptors>

<ref bean="soapAuth"/>

</jaxws:inInterceptors>

</jaxws:server>

那么endpoint和server这两种方式有啥区别?

 

 

 其实这两种方式就是刚学习发布第一个Webservice时可能编写的那两种方式的替换。

 

 写道
比如我们不通过Spring来简单发布一个Webservie,我们会这么做:

import javax.jws.WebParam;

import javax.jws.WebService;

@WebService

public interface HiService {

//to make sure the paramter is named correctly in the xml 

String sayHi(@WebParam(name="text") String text);

}

实现:

import javax.jws.WebService;

import javax.xml.ws.Endpoint;

import org.apache.cxf.interceptor.LoggingInInterceptor;

import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

/**

* A simple JAX-WS 规范的XML web services的JAVA API

*/

@WebService(endpointInterface="cn.jd.ws.HiService",serviceName="HiService")

public class HiServiceImpl implements HiService{

public static final String ENDPOINT = "http://localhost:9090/HiSerivice";

@Override

public String sayHi(String text) {

return "Hi " + text;

}

//这种方式就相当于是jaxws:endpoint

public void startServer() {

System.out.println("Starting the server");

HiServiceImpl hiService = new HiServiceImpl();

//通过Endpoint的方式直接就发布了

Endpoint.publish(ENDPOINT, hiService);

}

//这种方式就是jaxws:service方式

public void startServerNormal() {

      //这里利用的就是JaxWsServerFactoryBean

     JaxWsServerFactoryBean serverFactoryBean = new JaxWsServerFactoryBean();

     HiServiceImpl hiService = new HiServiceImpl();

    //服务类接口

    serverFactoryBean.setServiceClass(HiService.class);

   //设置地址

  serverFactoryBean.setAddress(ENDPOINT);

  serverFactoryBean.setServiceBean(hiService);

  serverFactoryBean.getInInterceptors().add(new LoggingInInterceptor());

  serverFactoryBean.create();

}

//发布服务

public static void main(String[] args) {

try {

  //能通过浏览器看到 是因为cxf自带了一个jetty服务器

  new HiServiceImpl().startServerNormal();

  System.out.println("发布服务成功");

}catch (Exception e) {

  System.out.println("发布服务失败");

}

}

}

 

 

然后就是配置CXFServlet的自启动:

 

 写道
<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring-config.xml</param-value></context-param>

<servlet>

<servlet-name>CXFServlet</servlet-name>

<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>CXFServlet</servlet-name>

<url-pattern>/soap/*</url-pattern>

</servlet-mapping>

 

 

这些都是简单的配置,接下来就该编写我们的安全验证类了:

 

其实理解安全机制实现的原理很简单:

 

   就是客户端的每一次请求,都要带着请求头,而服务端就去解析请求头,看里面带的token是否跟预期的一致,如果一致就说明安全了,

否则就抛出异常不让调用。

 

   那么就有两个操作:

1.在客户端发送webservice调用以前,构造一个SOAP消息头,把token带过去

2.在服务端解析消息头,把指定的那个头字段解析出来,对比 两边的token是否相同

 

 

 

 

首先是服务器端:

 

   写道

public class SOAPAuthIntercepter extends AbstractPhaseInterceptor<SoapMessage>{

private SAAJInInterceptor saaIn = new SAAJInInterceptor();

private String namespaceURI = "http://test.com/auth";

private String localPart = "MyAuthHeader";

public SOAPAuthIntercepter() {

//在哪个阶段被拦截 

super(Phase.PRE_PROTOCOL);

getAfter().add(SAAJInInterceptor.class.getName());

}

public void handleMessage(SoapMessage message) throws Fault {

System.out.println("=========>message:" + message);

if( !checkQnameHeader(message) && !checkMessageHeader(message)) {

throw new IllegalArgumentException("The Token wrong!");

}

}

**

* 校验指定的Qname头

* @param message

* @return

*/

private boolean checkQnameHeader(SoapMessage message) {

SoapHeader header = (SoapHeader)message.getHeader(new QName(namespaceURI, localPart));

if(header == null) {

return false;

}

ElementNSImpl ei = (ElementNSImpl) header.getObject(); 

String mytoken;

try {

mytoken = ei.getFirstChild().getFirstChild().getTextContent();

return mytoken.equals(token);

} catch (Exception e) {

throw new IllegalArgumentException("Method --> checkQnameHeader error",e);

}

}

/**

* 校验消息头

* @param message

* @return

*/

private boolean checkMessageHeader(SoapMessage message) {

try {

SOAPMessage mess = message.getContent(SOAPMessage.class);

if(mess == null) {

saaIn.handleFault(message); 

mess = message.getContent(SOAPMessage.class);

}

//获得SOAPHeader

SOAPHeader head = mess.getSOAPHeader();

if(head == null) return false;

//获得这个名称的NodeList

NodeList nodes = head.getElementsByTagName(localPart);

if(nodes == null) return false;

//取出来判断是否相等 

String mytoken = "";

for (int i = 0; i < nodes.getLength(); i++) {

mytoken += nodes.item(i).getTextContent();

}

return mytoken.equals(token);

} catch (Exception e) {

e.printStackTrace();

}

return false;

}

}

 

 

再就是客户端:

  写道

import javax.xml.namespace.QName;

import org.apache.cxf.binding.soap.SoapHeader;

import org.apache.cxf.binding.soap.SoapMessage;

import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;

import org.apache.cxf.helpers.DOMUtils;

import org.apache.cxf.helpers.XMLUtils;

import org.apache.cxf.interceptor.Fault;

import org.apache.cxf.phase.Phase;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

public class JdAuthOutInterceptor extends AbstractSoapInterceptor{

private AuthHeader authHeader;

public JdAuthOutInterceptor() {

super(Phase.WRITE); //在写之前

}

public void handleMessage(SoapMessage message) throws Fault {

//设定一个QName 这里的key就是localPart uri就是自定义的

QName qname = new QName(authHeader.getqName(), authHeader.getKey());

//构造一个XML

Document doc = DOMUtils.createDocument();

//创建给定的限定名称和名称空间 URI 的元素 这些操作都是jdk中的代码

Element authElement = doc.createElementNS(authHeader.getqName(),authHeader.getKey());

Element tokenElement = doc.createElement(authHeader.getToken());//令牌

tokenElement.setTextContent(authHeader.getTokenValue()); //设置值为

authElement.appendChild(tokenElement);

XMLUtils.printDOM(authElement);

//SOAP头 将有authElement元素组成

SoapHeader header = new SoapHeader(qname, authElement);

//把我们构造的这个头 添加到message中去

message.getHeaders().add(header);

}

public void setAuthHeader(AuthHeader authHeader) {

this.authHeader = authHeader;

}

}

 

构造一个SoapHeader对象:

 

  写道

import org.apache.commons.lang.StringUtils;

public class AuthHeader {

private final static String QNAME = "http://test.com/auth";

private String KEY = "MyAuthHeader";

private String TOKEN = "Token";

private String qName;

private String key;

private String token;

private String content;

public AuthHeader() {}

public String getqName() {

if(StringUtils.isEmpty(qName))

qName = QNAME;

return qName;

}

public void setqName(String qName) {

this.qName = qName;

}

public String getKey() {

if(StringUtils.isEmpty(key))

key = KEY;

return key;

}

public void setKey(String key) {

this.key = key;

}

public String getTokenValue() {

//令牌值

return content;

}

public String getToken() {

if(StringUtils.isEmpty(token)) {

token = TOKEN;

}

return token;

}

public void setToken(String token) {

this.token = token;

}

public void setContent(String content) {

this.content = content;

}

}

 

 

在Spring中配置一个客户端 ,启动服务后调用一下:

 

    写道

<bean id="mySoapAuth" class="cn.jd.ws.interceptor.JdAuthOutInterceptor">

<property name="authHeader" ref="authHeader"/>

</bean>

<!-- 客户端调用 -->

<jaxws:client id="dotaServiceClient" serviceClass="cn.jd.ws.SpringService" address="http://localhost:8080/Test/soap/DotaService">

<!-- 客户端outInterceptor 服务器端inInterceptor -->

<jaxws:outInterceptors>

<ref bean="mySoapAuth"/>

</jaxws:outInterceptors>

</jaxws:client>

 

 

 

 写道
public class SpringClientTest {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

SpringService service = (SpringService)context.getBean("dotaServiceClient");

System.out.println(service.play(" 5人黑 呵呵!"));

}

}

 

 

运行后客户端输出结果:

  写道

<?xml version="1.0" encoding="utf-8"?><MyAuthHeader xmlns="http://test.com/auth"><Token>ssssdddd</Token></MyAuthHeader>

Dota [ 5人黑 呵呵! ]

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复