Apache CXF實戰之六 創建安全的Web Service

我們在使用Web Service的過程中,很多情況是需要對web service請求做認證的,對於運行在web容器裏的應用程序來說,可能會比較簡單一些,通常可以通過filter來做一些處理,但是其實CXF本身也提供了對web service認證的方式。下面來看一下如何實現

1. 首先是一個簡單pojo

  1. package com.googlecode.garbagecan.cxfstudy.security; 
  2.  
  3. public class User { 
  4.     private String id; 
  5.     private String name; 
  6.     private String password; 
  7.     public String getId() { 
  8.         return id; 
  9.     } 
  10.     public void setId(String id) { 
  11.         this.id = id; 
  12.     } 
  13.     public String getName() { 
  14.         return name; 
  15.     } 
  16.     public void setName(String name) { 
  17.         this.name = name; 
  18.     } 
  19.     public String getPassword() { 
  20.         return password; 
  21.     } 
  22.     public void setPassword(String password) { 
  23.         this.password = password; 
  24.     } 
package com.googlecode.garbagecan.cxfstudy.security;

public class User {
    private String id;
    private String name;
    private String password;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
2. Web Service接口

  1. package com.googlecode.garbagecan.cxfstudy.security; 
  2.  
  3. import java.util.List; 
  4.  
  5. import javax.jws.WebMethod; 
  6. import javax.jws.WebResult; 
  7. import javax.jws.WebService; 
  8.  
  9. @WebService 
  10. public interface UserService { 
  11.     @WebMethod 
  12.     @WebResult List<User> list(); 
  13.  
package com.googlecode.garbagecan.cxfstudy.security;

import java.util.List;

import javax.jws.WebMethod;
import javax.jws.WebResult;
import javax.jws.WebService;

@WebService
public interface UserService {
    @WebMethod
    @WebResult List<User> list();

}
3. Web Service實現類

  1. package com.googlecode.garbagecan.cxfstudy.security; 
  2.  
  3. import java.util.ArrayList; 
  4. import java.util.List; 
  5.  
  6. public class UserServiceImpl implements UserService { 
  7.  
  8.     public List<User> list() { 
  9.         List<User> users = new ArrayList<User>(); 
  10.         for (int i = 0; i < 10; i++) { 
  11.             User user = new User(); 
  12.             user.setId("" + i); 
  13.             user.setName("user_" + i); 
  14.             user.setPassword("password_" + i); 
  15.             users.add(user); 
  16.         } 
  17.         return users; 
  18.     } 
  19.  
package com.googlecode.garbagecan.cxfstudy.security;

import java.util.ArrayList;
import java.util.List;

public class UserServiceImpl implements UserService {

    public List<User> list() {
        List<User> users = new ArrayList<User>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId("" + i);
            user.setName("user_" + i);
            user.setPassword("password_" + i);
            users.add(user);
        }
        return users;
    }

}
4. Server端Handler,其中使用了一個Map來存放用戶信息,真是應用中可以使用數據庫或者其它方式獲取用戶和密碼

  1. package com.googlecode.garbagecan.cxfstudy.security; 
  2.  
  3. import java.io.IOException; 
  4. import java.util.HashMap; 
  5. import java.util.Map; 
  6.  
  7. import javax.security.auth.callback.Callback; 
  8. import javax.security.auth.callback.CallbackHandler; 
  9. import javax.security.auth.callback.UnsupportedCallbackException; 
  10.  
  11. import org.apache.ws.security.WSPasswordCallback; 
  12.  
  13. public class ServerUsernamePasswordHandler implements CallbackHandler { 
  14.  
  15.     // key is username, value is password 
  16.     private Map<String, String> users; 
  17.  
  18.     public ServerUsernamePasswordHandler() { 
  19.         users = new HashMap<String, String>(); 
  20.         users.put("admin", "admin"); 
  21.     } 
  22.  
  23.     public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { 
  24.         WSPasswordCallback callback = (WSPasswordCallback) callbacks[0]; 
  25.         String id = callback.getIdentifier(); 
  26.         if (users.containsKey(id)) { 
  27.             if (!callback.getPassword().equals(users.get(id))) { 
  28.                 throw new SecurityException("Incorrect password."); 
  29.             } 
  30.         } else
  31.             throw new SecurityException("Invalid user."); 
  32.         } 
  33.     } 
package com.googlecode.garbagecan.cxfstudy.security;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

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

import org.apache.ws.security.WSPasswordCallback;

public class ServerUsernamePasswordHandler implements CallbackHandler {

    // key is username, value is password
    private Map<String, String> users;

    public ServerUsernamePasswordHandler() {
        users = new HashMap<String, String>();
        users.put("admin", "admin");
    }

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];
        String id = callback.getIdentifier();
        if (users.containsKey(id)) {
            if (!callback.getPassword().equals(users.get(id))) {
                throw new SecurityException("Incorrect password.");
            }
        } else {
            throw new SecurityException("Invalid user.");
        }
    }
}
5. Client端Handler,用來設置用戶密碼,在真實應用中可以根據此類和下面的測試類來修改邏輯設置用戶名和密碼。

  1. package com.googlecode.garbagecan.cxfstudy.security; 
  2.  
  3. import java.io.IOException; 
  4.  
  5. import javax.security.auth.callback.Callback; 
  6. import javax.security.auth.callback.CallbackHandler; 
  7. import javax.security.auth.callback.UnsupportedCallbackException; 
  8.  
  9. import org.apache.ws.security.WSPasswordCallback; 
  10.  
  11. public class ClientUsernamePasswordHandler implements CallbackHandler { 
  12.     public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { 
  13.         WSPasswordCallback callback = (WSPasswordCallback) callbacks[0]; 
  14.         int usage = callback.getUsage(); 
  15.         System.out.println("identifier: " + callback.getIdentifier()); 
  16.         System.out.println("usage: " + callback.getUsage()); 
  17.         if (usage == WSPasswordCallback.USERNAME_TOKEN) { 
  18.             callback.setPassword("admin"); 
  19.         } 
  20.     } 
package com.googlecode.garbagecan.cxfstudy.security;

import java.io.IOException;

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

import org.apache.ws.security.WSPasswordCallback;

public class ClientUsernamePasswordHandler implements CallbackHandler {
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];
        int usage = callback.getUsage();
        System.out.println("identifier: " + callback.getIdentifier());
        System.out.println("usage: " + callback.getUsage());
        if (usage == WSPasswordCallback.USERNAME_TOKEN) {
            callback.setPassword("admin");
        }
    }
}
6. 單元測試類,注意在Server端添加了WSS4JInInterceptor到Interceptor列表中,在Client添加了WSS4JOutInterceptor到Interceptor列表中。

  1. package com.googlecode.garbagecan.cxfstudy.security; 
  2.  
  3. import java.net.SocketTimeoutException; 
  4. import java.util.HashMap; 
  5. import java.util.List; 
  6. import java.util.Map; 
  7.  
  8. import javax.xml.ws.WebServiceException; 
  9.  
  10. import junit.framework.Assert; 
  11.  
  12. import org.apache.cxf.endpoint.Client; 
  13. import org.apache.cxf.endpoint.Endpoint; 
  14. import org.apache.cxf.frontend.ClientProxy; 
  15. import org.apache.cxf.interceptor.LoggingInInterceptor; 
  16. import org.apache.cxf.interceptor.LoggingOutInterceptor; 
  17. import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; 
  18. import org.apache.cxf.jaxws.JaxWsServerFactoryBean; 
  19. import org.apache.cxf.transport.http.HTTPConduit; 
  20. import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; 
  21. import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor; 
  22. import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor; 
  23. import org.apache.ws.security.WSConstants; 
  24. import org.apache.ws.security.handler.WSHandlerConstants; 
  25. import org.junit.BeforeClass; 
  26. import org.junit.Test; 
  27.  
  28. public class UserServiceTest { 
  29.  
  30.     private static final String address = "http://localhost:9000/ws/security/userService"
  31.      
  32.     @BeforeClass 
  33.     public static void setUpBeforeClass() throws Exception { 
  34.         JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean(); 
  35.         factoryBean.getInInterceptors().add(new LoggingInInterceptor()); 
  36.         factoryBean.getOutInterceptors().add(new LoggingOutInterceptor()); 
  37.  
  38.         Map<String, Object> props = new HashMap<String, Object>(); 
  39.         props.put("action", "UsernameToken"); 
  40.         props.put("passwordType", "PasswordText"); 
  41.         props.put("passwordCallbackClass", ServerUsernamePasswordHandler.class.getName()); 
  42.         WSS4JInInterceptor wss4JInInterceptor = new WSS4JInInterceptor(props); 
  43.         factoryBean.getInInterceptors().add(wss4JInInterceptor); 
  44.          
  45.         factoryBean.setServiceClass(UserServiceImpl.class); 
  46.         factoryBean.setAddress(address); 
  47.         factoryBean.create(); 
  48.     } 
  49.  
  50.     @Test 
  51.     public void testList() { 
  52.         JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean(); 
  53.         factoryBean.setAddress(address); 
  54.         factoryBean.setServiceClass(UserService.class); 
  55.         Object obj = factoryBean.create(); 
  56.          
  57.         Client client = ClientProxy.getClient(obj); 
  58.         Endpoint endpoint = client.getEndpoint(); 
  59.          
  60.         Map<String,Object> props = new HashMap<String,Object>(); 
  61.         props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); 
  62.         props.put(WSHandlerConstants.USER, "admin"); 
  63.         props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); 
  64.         props.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientUsernamePasswordHandler.class.getName()); 
  65.         WSS4JOutInterceptor wss4JOutInterceptor = new WSS4JOutInterceptor(props); 
  66.         endpoint.getOutInterceptors().add(wss4JOutInterceptor); 
  67.          
  68.         HTTPConduit conduit = (HTTPConduit) client.getConduit(); 
  69.         HTTPClientPolicy policy = new HTTPClientPolicy(); 
  70.         policy.setConnectionTimeout(5 * 1000); 
  71.         policy.setReceiveTimeout(5 * 1000); 
  72.         conduit.setClient(policy); 
  73.          
  74.         UserService service = (UserService) obj; 
  75.         try
  76.             List<User> users = service.list(); 
  77.             Assert.assertNotNull(users); 
  78.             Assert.assertEquals(10, users.size()); 
  79.         } catch(Exception e) { 
  80.             if (e instanceof WebServiceException  
  81.                     && e.getCause() instanceof SocketTimeoutException) { 
  82.                 System.err.println("This is timeout exception."); 
  83.             } else
  84.                 e.printStackTrace(); 
  85.             } 
  86.         } 
  87.     } 
  88.  
package com.googlecode.garbagecan.cxfstudy.security;

import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.ws.WebServiceException;

import junit.framework.Assert;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.junit.BeforeClass;
import org.junit.Test;

public class UserServiceTest {

    private static final String address = "http://localhost:9000/ws/security/userService";
    
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();
        factoryBean.getInInterceptors().add(new LoggingInInterceptor());
        factoryBean.getOutInterceptors().add(new LoggingOutInterceptor());

        Map<String, Object> props = new HashMap<String, Object>();
        props.put("action", "UsernameToken");
        props.put("passwordType", "PasswordText");
        props.put("passwordCallbackClass", ServerUsernamePasswordHandler.class.getName());
        WSS4JInInterceptor wss4JInInterceptor = new WSS4JInInterceptor(props);
        factoryBean.getInInterceptors().add(wss4JInInterceptor);
        
        factoryBean.setServiceClass(UserServiceImpl.class);
        factoryBean.setAddress(address);
        factoryBean.create();
    }

    @Test
    public void testList() {
        JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
        factoryBean.setAddress(address);
        factoryBean.setServiceClass(UserService.class);
        Object obj = factoryBean.create();
        
        Client client = ClientProxy.getClient(obj);
        Endpoint endpoint = client.getEndpoint();
        
        Map<String,Object> props = new HashMap<String,Object>();
        props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
        props.put(WSHandlerConstants.USER, "admin");
        props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
        props.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientUsernamePasswordHandler.class.getName());
        WSS4JOutInterceptor wss4JOutInterceptor = new WSS4JOutInterceptor(props);
        endpoint.getOutInterceptors().add(wss4JOutInterceptor);
        
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy policy = new HTTPClientPolicy();
        policy.setConnectionTimeout(5 * 1000);
        policy.setReceiveTimeout(5 * 1000);
        conduit.setClient(policy);
        
        UserService service = (UserService) obj;
        try {
            List<User> users = service.list();
            Assert.assertNotNull(users);
            Assert.assertEquals(10, users.size());
        } catch(Exception e) {
            if (e instanceof WebServiceException 
                    && e.getCause() instanceof SocketTimeoutException) {
                System.err.println("This is timeout exception.");
            } else {
                e.printStackTrace();
            }
        }
    }

}
最後運行上面的測試類來測試結果,也可以修改測試方法中的密碼,看看錯誤結果,這裏就不在寫錯誤密碼的測試用例了,因爲我是一懶人。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章