- 浏览: 5568 次
- 性别:
- 来自: 上海
最近访客 更多访客>>
文章分类
最新评论
-
ITForever:
楼主,使用chain的时候如何给里面的handler传参数?你 ...
Axis高级应用(二) -
sea0108:
非常实用。
Axis高级应用(二) -
GhostWolf:
String userId = msgContext.getU ...
Axis高级应用(二) -
shengfei:
之前写过一个webservices,当时觉得真简单,因为没 ...
Axis高级应用(二)
上篇简单的介绍了Handler的应用,本章将在上篇的基础上,加入Authentization这个概念并详细介绍Handler的使用.
Handler的基本概念
J2EE Web 服务中的Handler技术特点非常像Servlet技术中的Filter。我们知道,在Servlet中,当一个HTTP到达服务端时,往往要经过多个Filter对请求进行过滤,然后才到达提供服务的Servlet,这些Filter的功能往往是对请求进行统一编码,对用户进行认证,把用户的访问写入系统日志等。相应的,Web服务中的Handler通常也提供以下的功能:
对客户端进行认证、授权;
把用户的访问写入系统日志;
对请求的SOAP消息进行加密,解密;
为Web Services对象做缓存。
SOAP消息Handler能够访问代表RPC请求或者响应的SOAP消息。在JAX-RPC技术中,SOAP消息Handler可以部署在服务端,也可以在客户端使用。
SOAP消息Handler处理顺序
某个在线支付服务需要防止非授权的用户访问或者撰改服务端和客户端传输的信息,从而使用消息摘要(Message Digest)的方法对请求和响应的SOAP消息进行加密。当客户端发送SOAP消息时,Handler把请求消息中的某些敏感的信息(如信用卡密码)进行加密,然后把加密后的SOAP消息传输到服务端;服务端的SOAP消息Handler截取客户端的请求,把请求的SOAP 消息进行解密,然后把解密后的SOAP消息派发到目标的Web服务端点。
Apache axis是我们开发Web服务的较好的选择(当然也可以使用axis2,Metro这两个更为优秀安全的引擎),使用axisWeb服务开发工具,可以使用Handler来对服务端的请求和响应进行处理。典型的情况下,轴心点(pivot point)是Apache与提供程序功能相当的部分,通过它来和目标的Web服务进行交互,它通常称为Provider。axis中常用的Provider有Java:RPC,java:MSG,java:EJB。一个Web服务可以部署一个或者多个Handler。
Apache axis中的Handler体系结构和JAX-RPC 1.0(JSR101)中的体系结构稍有不同,需要声明的是,本文的代码在axis中开发,故需要在axis环境下运行。
下面的UserAuthentizationHandler类展示了一个简单的用户验证过程
public class UserAuthentizationHandler extends BasicHandler { public void invoke(MessageContext msgContext) throws AxisFault { SecurityProvider provider = (SecurityProvider) msgContext.getProperty("securityProvider"); if (provider == null) { provider = new SimpleSecurityProvider(); msgContext.setProperty("securityProvider", provider); } if (provider != null) { String userId = msgContext.getUsername(); // 对用户进行认证,如果authUser==null,表示没有通过认证,抛出Server.Unauthenticated异常。 org.apache.axis.security.AuthenticatedUser authUser = provider.authenticate(msgContext); if (authUser == null) throw new AxisFault("Server.Unauthenticated", Messages.getMessage("cantAuth01", userId), null, null); // 用户通过认证,把用户的设置成认证了的用户。 msgContext.setProperty("authenticatedUser", authUser); System.out.println( authUser.getName() + " 通过身份验证! "); } } }
deploy.wsdd中的描述
<!-- UserAuthentization用户认证 --> <handler name="UserAuthentization" type="java:handler.UserAuthentizationHandler"> </handler>
server-config.wsdd
<handler name="UserAuthentization" type="java:handler.UserAuthentizationHandler"/> <service name="MyService" provider="java:RPC"> <requestFlow> <handler type="Log"/> <handler type="UserAuthentization"/> </requestFlow> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="session"/> <parameter name="className" value="myservice.MyService"/> </service>
从代码中我们可以找到这么一句
String userId = msgContext.getUsername();
从soap消息中取得用户名. 有人会想,那密码在哪里取呢.其实.soap消息中也包含了客户端传送过来的密码,我们可以用
msgContext.getPassword();
方法取得.那用户名和密码在哪里设置的呢?下面我们来看看客户端如何调用的
public class Client { /** * @param args */ public static void main(String[] args) throws Exception { // 指出service所在URL String endpoint = "http://localhost:" + "8080" + "/axis_example/services/MyService"; // 创建一个服务(service)调用(call) Service service = new Service(); Call call = (Call) service.createCall();// 通过service创建call对象 // 设置service所在URL call.setTargetEndpointAddress(new java.net.URL(endpoint)); // 方法名(processService)与MyService.java方法名保持一致 call.setOperationName("processService"); /********************************************************/ // 设置访问时使用的用户名和密码 call.getMessageContext().setUsername("sosdairs"); call.getMessageContext().setPassword("123456"); /********************************************************/ // Object 数组封装了参数,参数为"This is Test!",调用processService(String arg) String ret = (String) call.invoke(new Object[] { "This is Test!", "" }); System.out.println(ret); } }
客户端程序在调用服务端方法之前,向soap中设置了Username和Passname.服务端UserAuthentizationHandler类中调用provider.authenticate(msgContext)该方法类验证客户端的身份.我们可以想象这个过程为一个远程登录的过程,客户端穿来用户名密码,服务端进行验证.下面,我们再来看看SecurityProvider,SecurityProvider是个接口,由SimpleSecurityProvider类来实现接口中的方法
public class SimpleSecurityProvider implements SecurityProvider { protected static Log log = LogFactory.getLog(SimpleSecurityProvider.class.getName()); HashMap users = null; HashMap perms = null; boolean initialized = false; // load the users list private synchronized void initialize(MessageContext msgContext) { if (initialized) return; String configPath = msgContext.getStrProp(Constants.MC_CONFIGPATH); if (configPath == null) { configPath = ""; } else { configPath += File.separator; } File userFile = new File(configPath + "users.lst"); if (userFile.exists()) { users = new HashMap(); try { FileReader fr = new FileReader( userFile ); LineNumberReader lnr = new LineNumberReader( fr ); String line = null ; // parse lines into user and passwd tokens and add result to hash table while ( (line = lnr.readLine()) != null ) { StringTokenizer st = new StringTokenizer( line ); if ( st.hasMoreTokens() ) { String userID = st.nextToken(); String passwd = (st.hasMoreTokens()) ? st.nextToken() : ""; users.put(userID, passwd); } } lnr.close(); } catch( Exception e ) { log.error( Messages.getMessage("exception00"), e ); return; } } initialized = true; } /** Authenticate a user from a username/password pair. * * @param username the user name to check * @param password the password to check * @return an AuthenticatedUser or null */ public AuthenticatedUser authenticate(MessageContext msgContext) { if (!initialized) { initialize(msgContext); } String username = msgContext.getUsername(); String password = msgContext.getPassword(); // in order to authenticate, the user must exist if ( username == null || username.equals("") || !users.containsKey(username) ) return null; String valid = (String) users.get(username); // if a password is defined, then it must match if ( valid.length()>0 && !valid.equals(password) ) return null; return new SimpleAuthenticatedUser(username); } return null; } }
authenticate方法中,从msgContext取得客户端传来的username,password同users中对应的key-value比较.users是个HashMap,在初始化方法中读users.lst这个文件.由此可知,我们必须在服务端存放一个users.lst文件,来保存用户名和密码.在应用的 */WEB-INF/目录下创建users.lst文件,内容为
sosdairs 123456 test 123456 tester 123456
启动你的服务,运行Client程序.log文件中将有你的访问信息.如果客户端设置的用户名密码不正确,则会返回Unauthenticated 错误信息
有了上面的用户认证,我们的服务就安全了吗?答案是否定的.有时候,我们还有必要对客户端访问服务端的服务做验证,这又称为授权.
假设有这么一个需求,一台主机提供了多个服务,这些服务都要求来访者进行身份确认(上面讲到的),其中有些服务所有通过验证的程序都可以进行访问,有个别服务只对特定身份的来访者开放.有了上面的启发,我们还可以做一个Handler,对服务进行授权访问.
axis提供了一个类,叫做SimpleSecurityProvider,可以用该类进行服务授权验证.为了方便展示,我将此类做了小小的修改,放在了我的应用中,并没有直接使用SimpleSecurityProvider类.请看代码
/** * 源代码请参考 axis-1_4\src\org\apache\axis\security\simple\SimpleSecurityProvider.java * @author sosdairs * */ public class RightAuthorizationHandler extends BasicHandler { public void invoke(MessageContext msgContext) throws AxisFault { boolean allowByDefault = JavaUtils .isTrueExplicitly(getOption("allowByDefault")); // 通过验证的用户 AuthenticatedUser user = (AuthenticatedUser) msgContext .getProperty(MessageContext.AUTHUSER); if (user == null) throw new AxisFault("Server.NoUser", Messages .getMessage("needUser00"), null, null); String userID = user.getName(); Handler serviceHandler = msgContext.getService(); if (serviceHandler == null) throw new AxisFault(Messages.getMessage("needService00")); String serviceName = serviceHandler.getName(); // 取得该服务授权的用户 String allowedRoles = (String) serviceHandler.getOption("allowedRoles"); if (allowedRoles == null) { if (allowByDefault) { System.out.println(" noRoles00 "); } else { throw new AxisFault("Server.Unauthorized", Messages.getMessage( "notAuth00", userID, serviceName), null, null); } return; } SecurityProvider provider = (SecurityProvider) msgContext .getProperty(MessageContext.SECURITY_PROVIDER); if (provider == null) throw new AxisFault(Messages.getMessage("noSecurity00")); StringTokenizer st = new StringTokenizer(allowedRoles, ","); while (st.hasMoreTokens()) { String thisRole = st.nextToken(); // 认证用户通过授权 if (provider.userMatches(user, thisRole)) { System.out.println(user.getName() + " 被允许访问 " + serviceHandler.getName() + " 服务 "); return; } } throw new AxisFault("Server.Unauthorized", Messages.getMessage( "cantAuth02", userID, serviceName), null, null); } }
deploy.wsdd
<!-- Authorization 认证后用户有权限才允许访问服务 --> <handler name="RightAuthorization" type="java:handler.RightAuthorizationHandler"> </handler> <service name="MyService" provider="java:RPC"> <parameter name="className" value="myservice.MyService" /> <parameter name="allowedMethods" value="*" /> <!-- 此服务允许sosdairs,test用户访问 --> <parameter name="allowedRoles" value="test,sosdairs"/> <!-- webservice服务中的object的生命周期 --> <!-- request, session, application --> <parameter name="scope" value="session"/> <requestFlow> <handler type="Log"/> <handler type="UserAuthentization"/> <handler type="RightAuthorization"/> </requestFlow> </service>
server-config.wsdd
<handler name="RightAuthorization" type="java:handler.RightAuthorizationHandler"/> <service name="MyService" provider="java:RPC"> <requestFlow> <handler type="Log"/> <handler type="UserAuthentization"/> <handler type="RightAuthorization"/> </requestFlow> <parameter name="allowedRoles" value="test,sosdairs"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="session"/> <parameter name="className" value="myservice.MyService"/> </service>
若在Client.java中,将用户名密码设置为tester 123456,运行Client,服务能通过其身份确认,但是其不可以访问服务.
至此,我们的服务具备了一些安全性,当然这种安全性是非常低的.SOAP消息是在http协议上传输的,而且soap是用XML的组织形式,并且采用的都是明文传输.
什么?明文!那我们刚才传输的用户名密码不都被别人看见了嘛! 没错.是被看见了.所以我们的服务很不安全.
咱们一点点来改进吧.出于对用户名密码的保护.我们可以将用户名,密码加密后再进行传输.这是一个很好的想法.加密解密.不单单对用户名密码进行加密解密,我们还可以对整个SOAP消息进行处理.只要能在客户端和服务端都实现特定的算法就可以了. 关于WEB安全技术,有很多途径可以实现:
WS-Security
WS-Trace
XML Digital Signature(XML数字签名)
XML Encryption (XML加密)
XKMS (XML Key Management Specification)
XACML (eXtensible Access Control Markup Language)
SAML (Secure Assertion Markup Language)
ebXML Message Service Security
Identity Management & Liberty Project
使用SSL/HTTPS协议来传输
本章在这里只是提及一下,在后来的章节中会有讲述,请大家关注
最后,我们再来认识一下chain.
Chain可以理解为它实现的一连串Handler的功能,就是多个Handler的集合.
package chain; import handler.RightAuthorizationHandler; import handler.UserAuthentizationHandler; import org.apache.axis.SimpleChain; public class SecurityChain extends SimpleChain{ private static final long serialVersionUID = 1L; public SecurityChain(){ UserAuthentizationHandler user = new UserAuthentizationHandler(); RightAuthorizationHandler right = new RightAuthorizationHandler(); this.addHandler(user); this.addHandler(right); } }
SimpleChain是继承BasicHandler的,SimpleChain也可以算是一个Handler
public class SimpleChain extends BasicHandler implements Chain { ....... }
deploy.wsdd
<chain name="SecurityChain" > <handler type="java:chain.SecurityChain"/> </chain> <requestFlow> <chain type="SecurityChain"/> <!-- <handler type="Log"/>--> <!-- <handler type="UserAuthentization"/>--> <!-- <handler type="RightAuthorization"/>--> </requestFlow>
这里只是把UserAuthentization和RightAuthorization放到了SecurityChain的构造方法里面,在服务发布的时候就自动加载了Handler.
通过上面的详细介绍,应该能够掌握Handler的用法.并且这些用法基本上能满足不是安全性很高的需求. 在后面的章节中,将介绍如何建立安全的AXIS服务
评论
老大 为什么 我这里打印出来的userId 为null呢 代码都和你的一样 真郁闷
看了lz的文章,得到不少帮助。只是没有关于ws-security的相关介绍,以及使用xml-security api的相关代码实例,有些遗憾。
相关推荐
3. **AXIS第三课:AXIS高级应用,使用Handler来增强Web服务的功能** Handler是Axis中一个强大的特性,允许开发者插入自定义逻辑在服务处理流程的各个阶段。你可以通过编写Handler来实现如认证、日志记录、消息转换...
#### 五、AXIS高级应用,在AXIS服务间传递JavaBean及其安全解决 **知识点概述:** 本节讨论如何在Axis服务之间传递JavaBean对象,并确保数据的安全性。 **知识点详述:** 1. **JavaBean的定义** - JavaBean是一...
- Axis2提供了更多高级功能,如数据绑定的选择(通过`-d`指定,可以选择`adb`、`xmlbeans`、`jibx`、`jaxme`和`jaxbri`)。 2. **部署描述文件的生成**: - 在Axis中,可以通过添加`-D`选项来生成部署描述文件...
本文将详细介绍Axis2的API及其在Web服务开发中的应用,同时也会探讨Axis2的帮助文档如何协助开发者更好地理解和使用这个框架。 ### Axis2简介 Axis2是Apache Axis的第二代版本,它在第一代的基础上进行了许多改进和...
4. **高级功能**:包括MTOM(Message Transmission Optimization Mechanism)和SWA(Soap with Attachments)支持,用于高效传输二进制数据。 5. **部署灵活性**:Axis2支持多种部署方式,如WAR文件、AXIS2.xml配置...
【标题】"Axis教程文档...通过这份"Axis教程文档大全",开发者不仅能掌握Axis的基本用法,还能学习到Web服务开发的高级技巧,从而在工作中更加游刃有余。无论是自学还是团队培训,这样的资源都是不可多得的学习宝藏。
本文档将深入探讨Axis2的核心概念、安装、部署以及高级应用,旨在帮助开发者全面了解和熟练运用Axis2。 **一、Axis2简介** Axis2是Apache Axis的第二代产品,设计上更加优化,支持多语言环境,包括Java、C++和PHP。...
Axis2支持更多高级Web服务标准,如WS-Addressing和WS-ReliableMessaging。 - **Axis2 Transports**: 一系列传输模块,包括HTTP、HTTPS、JMS等,用于发送和接收SOAP消息。 - **AXIOM (Apache XML Objects for ...
总的来说,【Axis入门中文文档】是学习和实践Web服务开发的重要参考资料,涵盖了从基础概念到高级应用的全面内容,对于理解和掌握Web服务技术具有极大的帮助。通过深入学习和实践,开发者可以更加熟练地运用Axis构建...
在IBM DeveloperWorks上,你可以找到一系列的文章,涵盖了从Axis2的安装、配置到高级特性的使用。这些文章可能会详细讲解如何创建服务部署单元(Service Archive, AAR)、如何配置模块、如何处理异常以及如何利用...
在IT行业中,Web服务是应用程序之间进行通信的一种标准方式,而Axis2是Apache软件基金会开发的一个流行的Java Web服务框架。Spring框架则是Java企业级应用的事实标准,提供了强大的依赖注入、AOP(面向切面编程)等...
在实际项目中, Axis1.4还可以与其他技术结合,例如Spring框架,实现更高级的功能,如依赖注入和事务管理。同时,对于安全性,可以通过设置HTTPS和数字证书来增强Web服务的安全性。 总之,Axis1.4是Java Web ...
这个"Axis2例子Demo"旨在帮助开发者快速理解和应用Axis2框架,解决他们在尝试 Axis2 示例时可能遇到的问题。 **一、Axis2简介** Axis2是基于SOAP(简单对象访问协议)的Web服务框架,它构建在AXIOM(抽象XML信息...
学习者需要有一定的Axis2基础和Web服务概念,以便更好地理解和应用这些示例。 标签 "axis2" 明确地指出这个项目与Apache Axis2紧密相关。Axis2是基于SOAP(简单对象访问协议)的,它是用于在不同系统间交换结构化...
- 高级特性,如MTOM(Message Transmission Optimization Mechanism)和SwA(Soap with Attachments)用于传输二进制数据。 - 服务的发布和部署,包括WAR和AAR包的创建。 - 服务版本控制和多版本并存策略。 - ...
1. ** Axis2介绍**:Axis2是Apache SOAP(简单对象访问协议)引擎的第二代,它支持SOAP 1.1和1.2,同时提供了RESTful服务的支持。它基于模块化架构,使得服务部署和扩展更加灵活。 2. ** 安装和配置**:学习如何在...
通过这个“Axis教程集合”,你将能够全面了解和掌握Axis的各个方面,从基础的使用到高级的定制,帮助你在Web服务开发领域更加得心应手。无论是快速创建原型,还是构建复杂的分布式系统,Axis都是一个值得信赖的工具...
11. **安全**:虽然Axis1.4本身不包含高级的安全特性,但它可以与其他安全框架(如Spring Security或WS-Security)集成,以实现身份验证、授权和消息加密。 12. **调试与日志**:Axis1.4提供了丰富的日志记录和调试...