您的位置:首页 > 编程语言 > Java开发

JDK1.6自带的Jax-Ws详解(含权限检查完整解决方案)

2010-11-01 20:23 267 查看
       最近参与开发一个防火墙项目,主要负责Web Services,因为实际情况选用了JDK1.6自带的Jax-Ws,它比当前比较主流的CXF(已经替代了以前的XFIRE)轻量,不依赖任何容器,一个简单的Java Application就可以发布Web Services,这是一件多么令人兴奋的事,但是权限检查却非常费力,查看了很多解决方案,他们大多数是针对容器上部署的Web Services的权限检查......最后终于在官网文档(https://xwss.dev.java.net/Securing_JAVASE6_WebServices.html)上找到了解决思路,通过不断尝试得到一个比较完整的Jax-Ws权限检查方案.

       要使用Jax-Ws,首先得引入相关Jar包(webservices-api.jar、webservices-rt.jar、webservices-tools.jar),下面是一个简单定义Web Services接口的实现类:
import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.WebParam.Mode;
import javax.jws.soap.SOAPBinding;
import javax.xml.ws.WebServiceContext;
import com.sun.xml.wss.SubjectAccessor;

@WebService(name="Processor",targetNamespace="http://www.qingyh.fimas.command",serviceName="Processor")
@SOAPBinding(style=SOAPBinding.Style.RPC)
public class Processor {
@Resource WebServiceContext context;

@WebMethod(operationName="start",action="start",exclude=false)
@WebResult(name="returnStart")
public String start() {
try {
System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context));
} catch (Exception e) {
throw new RuntimeException(e);
}

//................To Achieve In Detail.....................
return "数据";
}

@WebMethod(operationName="getListernerByFw",action="getListernerByFw",exclude=false)
@WebResult(name="returnGetListernerByFw")
public String getListernerByFw(@WebParam(partName="id",mode=Mode.IN)String id,@WebParam(partName="type",mode=Mode.IN)String type) {
try {
System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context));
} catch (Exception e) {
throw new RuntimeException(e);
}
//................To Achieve In Detail.....................
return type+"=。。。。。。";
}

@WebMethod(operationName="executeCMD",action="executeCMD",exclude=false)
@WebResult(name="returnExecuteCMD")
public String execute(@WebParam(partName="ucmd",mode=Mode.IN)String ucmd,@WebParam(partName="firewallID",mode=Mode.IN)String firewallID) {
try {
System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context));
} catch (Exception e) {
throw new RuntimeException(e);
}

//................To Achieve In Detail.....................
return "Congratulations,your"+ucmd+"has executed successfully on firewall"+firewallID;
}

}


然后是发布Web Services的main方法:
import java.util.LinkedList;
import java.util.List;
import javax.xml.ws.Binding;
import javax.xml.ws.Endpoint;
import javax.xml.ws.handler.Handler;

public class StartServer {

public static void main(String[] args) {

Endpoint endpoint = Endpoint.create(new Processor());
Binding binding = endpoint.getBinding();
List<Handler> handlerChain = new LinkedList<Handler>();
handlerChain.add(new SecurityHandler("server"));
binding.setHandlerChain(handlerChain);
endpoint.publish("http://192.168.1.109:8888/fimas");
System.out.println("Fimas is Working:http://192.168.1.109:8888/fimas");
}

}


其中192.168.1.109为本机的IP,8888为发布Web Services的端口号,一般运行这个main方法(加上下文的权限检查文件后),你的Web Services就算发布成功了(通过浏览器访问http://192.168.1.109:8888/fimas?wsdl即可看到wsdl文件,说明发布成功),但是如果你的接口类Processor里使用了自定义的异常类,则还得操作如下生成异常Bean(运行main才不会出错):首先在你的java项目根目录下新建一个文件夹(名字为wsdl,它和src同级)存放wsdl文件,然后通过cmd----》cd   XXXXXXXXXX进入你的java项目根路径,然后执行命令:
wsgen -cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl  Processor的包名.Processor


,这样通过JDK自带的wsgen工具就会自动在项目中生成异常Bean类和wsdl文件,这样Web Services就会成功发布了.

       下面才是重点关于权限检查:
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import com.sun.xml.wss.impl.callback.PasswordCallback;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
import com.sun.xml.wss.impl.callback.UsernameCallback;

public class SecurityEnvironmentHandler implements CallbackHandler {

private static final UnsupportedCallbackException unsupported = new UnsupportedCallbackException(
null, "Unsupported Callback Type Encountered");

/** Creates a new instance of SecurityEnvironmentHandler */
public SecurityEnvironmentHandler(String arg) {
}

public String getPassword(String userName) {
//..............根据用户名从数据库获得相应的密码...........................
return "";
}

public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof PasswordValidationCallback) {
PasswordValidationCallback cb = (PasswordValidationCallback) callbacks[i];
if (cb.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
cb.setValidator(new PlainTextPasswordValidator());

} else if (cb.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
PasswordValidationCallback.DigestPasswordRequest request = (PasswordValidationCallback.DigestPasswordRequest) cb
.getRequest();
String username = request.getUsername();
String pwd = getPassword(username);
if (pwd != "" && pwd != null) {
request.setPassword(pwd);
cb.setValidator(new PasswordValidationCallback.DigestPasswordValidator());
}
}
} else if (callbacks[i] instanceof UsernameCallback) {
UsernameCallback cb = (UsernameCallback) callbacks[i];
String username = (String) cb.getRuntimeProperties().get(
com.sun.xml.wss.XWSSConstants.USERNAME_PROPERTY);
System.out.println("Got Username......... : " + username);
cb.setUsername(username);

} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback cb = (PasswordCallback) callbacks[i];
String password = (String) cb.getRuntimeProperties().get(
com.sun.xml.wss.XWSSConstants.PASSWORD_PROPERTY);
System.out.println("Got Password......... : " + password);
cb.setPassword(getMd5(password));

} else {
throw unsupported;
}
}
}

private class PlainTextPasswordValidator implements
PasswordValidationCallback.PasswordValidator {
public boolean validate(PasswordValidationCallback.Request request)
throws PasswordValidationCallback.PasswordValidationException {

PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest = (PasswordValidationCallback.PlainTextPasswordRequest) request;
String pwd = getPassword(plainTextRequest.getUsername());
if (pwd != null && pwd != ""
&& pwd.equals(getMd5(plainTextRequest.getPassword()))) {
return true;
}
return false;
}
}

/**
* MD5加密
*
* @param password
* @return
*/
public String getMd5(String password) {
String Md5Password = "";
try {
java.security.MessageDigest encryptMethod = java.security.MessageDigest
.getInstance("MD5");
encryptMethod.update(password.getBytes());
Md5Password = byte2hex(encryptMethod.digest());
} catch (Exception e) {
e.printStackTrace();
}

return Md5Password;
}

/**
* 二进制转字符串
*
* @param b
* @return
*/
public String byte2hex(byte[] b) {
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (java.lang.Integer.toHexString(b
& 0XFF));
if (stmp.length() == 1)
hs = hs + "0" + stmp;
else
hs = hs + stmp;
}

return hs.toLowerCase();
}
}


      
import java.io.InputStream;
import java.util.Set;
import java.util.HashSet;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import com.sun.xml.wss.ProcessingContext;
import com.sun.xml.wss.SubjectAccessor;
import com.sun.xml.wss.XWSSProcessorFactory;
import com.sun.xml.wss.XWSSProcessor;
import com.sun.xml.wss.XWSSecurityException;

public class SecurityHandler implements SOAPHandler<SOAPMessageContext> {

XWSSProcessor sprocessor = null;
String clientOrServer = null;
XWSSProcessor cprocessor = null;

/** Creates a new instance of SecurityHandler */
public SecurityHandler(String cOrs) {
InputStream serverConfig = null;
InputStream clientConfig = null;
this.clientOrServer = cOrs;	try {
if ("client".equals(this.clientOrServer)) {
// read client side security config
clientConfig = new java.io.FileInputStream(
new java.io.File("META-INF/user-pass-authenticate-client.xml"));
//clientConfig =getClass().getResourceAsStream("/cn/com/letour/fimascontroller/META-INF/user-pass-authenticate-client.xml");
// Create a XWSSProcessFactory.
XWSSProcessorFactory factory = XWSSProcessorFactory
.newInstance();
cprocessor = factory.createProcessorForSecurityConfiguration(
clientConfig, new SecurityEnvironmentHandler("client"));
clientConfig.close();
} else {
// read server side security configuration
serverConfig = new java.io.FileInputStream(
new java.io.File("META-INF/user-pass-authenticate-server.xml"));
//serverConfig = getClass().getResourceAsStream("/cn/com/letour/fimascontroller/META-INF/user-pass-authenticate-server.xml");
// Create a XWSSProcessFactory.
XWSSProcessorFactory factory = XWSSProcessorFactory
.newInstance();
sprocessor = factory.createProcessorForSecurityConfiguration(
serverConfig, new SecurityEnvironmentHandler("server"));
serverConfig.close();			}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}

}

public Set<QName> getHeaders() {
QName securityHeader = new QName(
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"Security", "wsse");
HashSet<QName> headers = new HashSet<QName>();
headers.add(securityHeader);
return headers;
}

public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}

public boolean handleMessage(SOAPMessageContext messageContext) {
if ("client".equals(this.clientOrServer)) {
secureClient(messageContext);
} else {
secureServer(messageContext);
}
return true;
}

public void close(MessageContext messageContext) {
}

@SuppressWarnings("unchecked")
private void secureServer(SOAPMessageContext messageContext) {
Boolean outMessageIndicator = (Boolean) messageContext
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = messageContext.getMessage();

if (outMessageIndicator.booleanValue()) {
System.out.println("/nOutbound SOAP:");
// do nothing....
return;
} else {
System.out.println("/nInbound SOAP:");
// verify the secured message.
try {
ProcessingContext context = sprocessor
.createProcessingContext(message);
context.getExtraneousProperties().putAll(messageContext);
context.setSOAPMessage(message);
SOAPMessage verifiedMsg = null;
verifiedMsg = sprocessor.verifyInboundMessage(context);
System.out.println("/nRequester Subject "
+ SubjectAccessor.getRequesterSubject(context));
messageContext.setMessage(verifiedMsg);
} catch (XWSSecurityException ex) {
// create a Message with a Fault in it
// messageContext.setMessage(createFaultResponse(ex));
ex.printStackTrace();
throw new WebServiceException(ex);
} catch (Exception ex) {
ex.printStackTrace();
throw new WebServiceException(ex);
}
}
}

@SuppressWarnings("unused")
private SOAPMessage createFaultResponse(XWSSecurityException ex) {
// TODO: add code here
return null;
}

private void secureClient(SOAPMessageContext messageContext) {
Boolean outMessageIndicator = (Boolean) messageContext
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = messageContext.getMessage();
if (outMessageIndicator.booleanValue()) {
System.out.println("/nOutbound SOAP:");
ProcessingContext context;
try {
context = cprocessor.createProcessingContext(message);
context.setSOAPMessage(message);
SOAPMessage secureMsg = cprocessor
.secureOutboundMessage(context);
secureMsg.writeTo(System.out);
messageContext.setMessage(secureMsg);
} catch (XWSSecurityException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return;
} else {
System.out.println("/nInbound SOAP:");
// do nothing
return;
}
}

}


在Processor.java同级下加入上面的SecurityEnvironmentHandler.java和SecurityHandler.java两个文件,还有在java项目根路径下新建一个包(包名为META-INF,它和src同级),META-INF里面放着两个文件user-pass-authenticate-client.xml(里面的内容是:
<!--

Copyright 2004 Sun Microsystems, Inc. All rights reserved.
SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

-->

<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config" dumpMessages="true" >
<xwss:UsernameToken digestPassword="false"/>
</xwss:SecurityConfiguration>
)和user-pass-authenticate-server.xml(里面的内容是:
<!--

Copyright 2004 Sun Microsystems, Inc. All rights reserved.
SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

-->
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config" dumpMessages="true" >
<xwss:RequireUsernameToken passwordDigestRequired="false"/>
</xwss:SecurityConfiguration>


这样一个含身份验证的Web Services服务器端已经构建成功。

 

 

 

客户端:

首先访问http://192.168.1.109:8888/fimas?wsdl看看服务器端是否发布成功Web Services,然后cmd---》cd XXXXXXXX进入本java项目某一个包下通过JDK工具生成本地web services调用文件,执行
wsimport -d ./bin -s ./src -p  你的包名 http://192.168.1.109:8888/fimas?wsdl
     

客户端的权限检查文件(有部分代码和服务器端代码不一样):

 
import java.io.FileInputStream;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import com.sun.xml.wss.ProcessingContext;
import com.sun.xml.wss.SubjectAccessor;
import com.sun.xml.wss.XWSSProcessor;
import com.sun.xml.wss.XWSSProcessorFactory;
import com.sun.xml.wss.XWSSecurityException;

@SuppressWarnings("restriction")
public class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
String username;
String password;
XWSSProcessor sprocessor = null;
String clientOrServer = null;
XWSSProcessor cprocessor = null;
/** Creates a new instance of SecurityHandler */

public SecurityHandler(String cOrs,String username,String password) {
this.username=username;
this.password=password;
FileInputStream serverConfig = null;
FileInputStream clientConfig = null;
this.clientOrServer = cOrs;
try {
if ("client".equals(this.clientOrServer)) {
//read client side security config
clientConfig = new java.io.FileInputStream(
new java.io.File("META-INF/user-pass-authenticate-client.xml"));
//Create a XWSSProcessFactory.
XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance();
cprocessor = factory.createProcessorForSecurityConfiguration(
clientConfig, new SecurityEnvironmentHandler("client",username,password));
clientConfig.close();
} else {
//read server side security configuration
serverConfig = new java.io.FileInputStream(
new java.io.File("META-INF/user-pass-authenticate-server.xml"));
//Create a XWSSProcessFactory.
XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance();
sprocessor = factory.createProcessorForSecurityConfiguration(
serverConfig, new SecurityEnvironmentHandler("server",username,password));
serverConfig.close();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}

}

public Set<QName> getHeaders() {
QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse");
HashSet<QName> headers = new HashSet<QName>();
headers.add(securityHeader);
return headers;
}

@SuppressWarnings("restriction")
public boolean handleFault(SOAPMessageContext messageContext) {
return true;
}

public boolean handleMessage(SOAPMessageContext messageContext) {
if ("client".equals(this.clientOrServer)) {
secureClient(messageContext);
} else {
secureServer(messageContext);
}
return true;
}
public void close(MessageContext messageContext) {}

private void secureServer(SOAPMessageContext messageContext)
{
Boolean outMessageIndicator = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = messageContext.getMessage();

if (outMessageIndicator.booleanValue()) {
System.out.println("/nOutbound SOAP:");
// do nothing....
return;
} else {
System.out.println("/nInbound SOAP:");
//verify the secured message.
try{
ProcessingContext context =  sprocessor.createProcessingContext(message);
context.setSOAPMessage(message);
SOAPMessage verifiedMsg= null;
verifiedMsg= sprocessor.verifyInboundMessage(context);
System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context));
messageContext.setMessage(verifiedMsg);
} catch (XWSSecurityException ex) {
//create a Message with a Fault in it
//messageContext.setMessage(createFaultResponse(ex));
ex.printStackTrace();
throw new WebServiceException(ex);
} catch(Exception ex){
ex.printStackTrace();
throw new WebServiceException(ex);
}
}
}

@SuppressWarnings("unused")
private SOAPMessage createFaultResponse(XWSSecurityException ex) {
// TODO: add code here
return null;
}
@SuppressWarnings("unchecked")
private void secureClient(SOAPMessageContext messageContext) {
Boolean outMessageIndicator = (Boolean)
messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessage message = messageContext.getMessage();
if (outMessageIndicator.booleanValue()) {
System.out.println("/nOutbound SOAP:");
ProcessingContext context;
try {
context = cprocessor.createProcessingContext(message);
context.setSOAPMessage(message);
SOAPMessage secureMsg = cprocessor.secureOutboundMessage(context);
context.getExtraneousProperties().putAll(messageContext);
secureMsg.writeTo(System.out);
messageContext.setMessage(secureMsg);
} catch (XWSSecurityException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return;
} else {
System.out.println("/nInbound SOAP:");
//do nothing
return;
}
}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import com.sun.xml.wss.impl.callback.PasswordCallback;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
import com.sun.xml.wss.impl.callback.UsernameCallback;

public class SecurityEnvironmentHandler implements CallbackHandler {
String username;
String password;

private static final UnsupportedCallbackException unsupported = new UnsupportedCallbackException(null,
"Unsupported Callback Type Encountered");

/** Creates a new instance of SecurityEnvironmentHandler */
public SecurityEnvironmentHandler(String arg, String username, String password) {
this.username = username;
this.password = password;
}

private String readLine() throws IOException {
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof PasswordValidationCallback) {
PasswordValidationCallback cb = (PasswordValidationCallback) callbacks[i];
if (cb.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
cb.setValidator(new PlainTextPasswordValidator());

} else if (cb.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
PasswordValidationCallback.DigestPasswordRequest request = (PasswordValidationCallback.DigestPasswordRequest) cb
.getRequest();
String username1 = request.getUsername();
if (username.equals(username1)) {
request.setPassword(password);
cb.setValidator(new PasswordValidationCallback.DigestPasswordValidator());
}
}
} else if (callbacks[i] instanceof UsernameCallback) {
UsernameCallback cb = (UsernameCallback) callbacks[i];
// String username =
// (String)cb.getRuntimeProperties().get(com.sun.xml.wss.XWSSConstants.USERNAME_PROPERTY);
// String username="Fimas";
System.out.println("Got Username......... : " + username);
cb.setUsername(username);

} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback cb = (PasswordCallback) callbacks[i];
// String password =
// (String)cb.getRuntimeProperties().get(com.sun.xml.wss.XWSSConstants.PASSWORD_PROPERTY);
// String password="samiF";
System.out.println("Got Password......... : " + password);
cb.setPassword(password);

}

else {
throw unsupported;
}
}
}

private class PlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator {
public boolean validate(PasswordValidationCallback.Request request)
throws PasswordValidationCallback.PasswordValidationException {

PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest = (PasswordValidationCallback.PlainTextPasswordRequest) request;
if (username.equals(plainTextRequest.getUsername()) && password.equals(plainTextRequest.getPassword())) {
return true;
}
return false;
}
}
}


 

 在该java项目根路径下新建一个文件夹META-INF(它仍然和src同级) ,其内容与服务器端的META-INF内容一样,最后是客户端测试文件:
import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;

public class RunClient {

public static void main(String[] args) {
Processor_Service service=new Processor_Service();
Processor processor=service.getProcessorPort();

List<Handler> chain=new ArrayList<Handler>();
chain.add(new SecurityHandler("client","admin","admin"));
((BindingProvider)processor).getBinding().setHandlerChain(chain);
System.out.println(processor.getListernerByFw("100","it is a test!"));
System.out.println(processor.execute("UCMD","200"));
}

}


服务器端数据库权限控制表内必须有username:admin  password:admin该用户,客户端才能成功调用服务器端发布的Web Services(即客户端通过服务器端授权(密码经过MD5加密)).

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息