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中的配置:

  写道

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包下的文件:


 

 

在spring中发布服务:

 

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


 


参数implementor指定这个发布出来的WebService服务的实现类是哪个,address表示访问的地址,其中的拦截器就是我们需要将的那个安全验证的拦截器。稍后会介绍到。
这里配置了一个令牌,当然为了安全最好是通过MD5等安全加密算法加密过的。为了简单直接搞了一个。

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







那么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的自启动:

 

 写道

org.springframework.web.context.ContextLoaderListener



contextConfigLocation
classpath:spring-config.xml




CXFServlet
org.apache.cxf.transport.servlet.CXFServlet
1



CXFServlet
/soap/*

 

 

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

 

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

 

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

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

 

   那么就有两个操作:

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

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

 

 

 

 

首先是服务器端:

 

   写道

public class SOAPAuthIntercepter extends AbstractPhaseInterceptor{

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中配置一个客户端 ,启动服务后调用一下:

 

    写道











 

 

 

 写道
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人黑 呵呵!"));
}
}

 

 

运行后客户端输出结果:

  写道

ssssdddd
Dota [ 5人黑 呵呵! ]

你可能感兴趣的:(Cxf Webservice安全认证)