Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Cxf Webservice安全认证,希望能够帮助你!!!。
在开发的时候发布webservice,为了安全通常需要安全验证,在CXF中的实现,在这里记录一下。
CXF是啥 我就不介绍了, 开发CXF+Spring的webservice服务:
在这里发布一个简单的服务,比如发布的服务为SpingService
import javax.jws.WebService;
@WebService
public interface SpringService {
String play(String info);
}
具体的实现类:
写道
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中的配置:
写道
写道
在spring中发布服务:
<jaxws:server id="" serviceClass="cn.jd.ws.DotaSpringService" address="/DataService">
<jaxws:inInterceptors>
<ref bean="soapAuth"/>
</jaxws:inInterceptors>
</jaxws:server>
那么endpoint和server这两种方式有啥区别?
其实这两种方式就是刚学习发布第一个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的自启动:
<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是否相同
首先是服务器端:
写道
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对象:
写道
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中配置一个客户端 ,启动服务后调用一下:
写道
<!-- 客户端调用 -->
<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 static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
SpringService service = (SpringService)context.getBean("dotaServiceClient");
System.out.println(service.play(" 5人黑 呵呵!"));
}
}
运行后客户端输出结果:
写道
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章