嘮叨兩句java
講真,SOAP跟如今流行的RESTful WebService比起來顯得很難用。冗餘的XML文本信息太多,可讀性差,它的請求信息有時很難手動構造,不太好調試。不過說歸說,對某些企業用戶來講SOAP的使用率仍然是很高的。
需求背景spring
接手維護的一個項目,最近客戶想開放項目中的功能給第三方調用,並且接入方指定必須是SOAP接口。這項目原來的代碼我看着頭疼,也不想再改了,除非推倒重寫。客戶的需求雖然不難但要的很急,爲了儘快交付就使用SpringBoot快速搭一個微服務。
開始動手apache
1.新建一個Spring Starter Project 2.加入cxf的maven配置 <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.1.15</version> </dependency>
編寫服務代碼(示例代碼)app
@WebService(targetNamespace="http://demo.example.com/") public interface IUserService { @WebMethod User getUserById(@WebParam(name = "id") int id); @WebMethod int addUser(@WebParam(name = "user") User user); }
@InInterceptors(interceptors={"com.example.demo.auth.AuthInterceptor"}) @WebService(serviceName = "UserService", targetNamespace = "http://demo.example.com/", endpointInterface = "com.example.demo.soap.IUserService") @Component public class UserServiceImpl implements IUserService { private Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private IUserDAO userDAO; @Override public User getUserById(int id) { return userDAO.getUserById(id); } @Override public int addUser(User user) { logger.info("save user [" + user.getId() + "]"); userDAO.addUser(user); return 0; } }
鑑權攔截器maven
public class AuthInterceptor extends AbstractSoapInterceptor { private static final String BASIC_PREFIX = "Basic "; private static final String USERNAME = "lichmama"; private static final String PASSWORD = "123456"; public AuthInterceptor() { super(Phase.PRE_INVOKE); } @Override public void handleMessage(SoapMessage message) throws Fault { HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST); String auth = request.getHeader("Authorization"); if (auth == null) { SOAPException exception = new SOAPException("auth failed, header [Authorization] not exists"); throw new Fault(exception); } if (!auth.startsWith(BASIC_PREFIX)) { SOAPException exception = new SOAPException("auth failed, header [Authorization] is illegal"); throw new Fault(exception); } String plaintext = new String(Base64.getDecoder().decode(auth.substring(BASIC_PREFIX.length()))); if (StringUtils.isEmpty(plaintext) || !plaintext.contains(":")) { SOAPException exception = new SOAPException("auth failed, header [Authorization] is illegal"); throw new Fault(exception); } String[] userAndPass = plaintext.split(":"); String username = userAndPass[0]; String password = userAndPass[1]; if (!USERNAME.equals(username) || !PASSWORD.equals(password)) { SOAPException exception = new SOAPException("auth failed, username or password is incorrect"); throw new Fault(exception); } } }
編寫配置類ide
@Configuration public class SoapConfig { @Autowired private IUserService userService; @Autowired @Qualifier(Bus.DEFAULT_BUS_ID) private SpringBus bus; @Bean public Endpoint endpoint() { EndpointImpl endpoint = new EndpointImpl(bus, userService); endpoint.publish("/userService"); return endpoint; } }
修改CXF默認發佈路徑(application.properties)spring-boot
server.port=8000
cxf.path=/soap
啓動項目後訪問http://localhost:8000/soap/userService?wsdl微服務
使用SoapUI測試一下,看上去沒什麼問題測試
客戶端增長對Basic Auth的支持:ui
/* 使用Eclipse自動生成Web Service Client,在SoapBingdingStub的createCall()方法中加入一下代碼: */ // basic auth _call.setProperty("javax.xml.rpc.security.auth.username", "lichmama"); _call.setProperty("javax.xml.rpc.security.auth.password", "123456");
而後正常調用便可,這裏提供一下本身寫的SoapClient:
public class GeneralSoapClient { private String WSDL; private String namespaceURI; private String localPart; public GeneralSoapClient() { } public GeneralSoapClient(String WSDL, String namespaceURI, String localPart) { this.WSDL = WSDL; this.namespaceURI = namespaceURI; this.localPart = localPart; } public String getWSDL() { return WSDL; } public void setWSDL(String WSDL) { this.WSDL = WSDL; } public String getNamespaceURI() { return namespaceURI; } public void setNamespaceURI(String namespaceURI) { this.namespaceURI = namespaceURI; } public String getLocalPart() { return localPart; } public void setLocalPart(String localPart) { this.localPart = localPart; } private boolean requireAuth = false; private String username; private String password; public boolean isRequireAuth() { return requireAuth; } public void setRequireAuth(boolean requireAuth) { this.requireAuth = requireAuth; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } /** * 建立Soap Service實例 * @param serviceInterface * @return * @throws Exception */ public <T> T create(Class<T> serviceInterface) throws Exception { URL url = new URL(WSDL); QName qname = new QName(namespaceURI, localPart); Service service = Service.create(url, qname); T port = service.getPort(serviceInterface); if (requireAuth) { BindingProvider prov = (BindingProvider) port; prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username); prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password); } return port; } }
public class TestSoap { public static void main(String[] args) throws Exception { String wsdl = "http://localhost:8080/soap/userService?wsdl"; String namespaceURI = "http://demo.example.com/"; String localPart = "UserService"; GeneralSoapClient soapClient = new GeneralSoapClient(wsdl, namespaceURI, localPart); soapClient.setRequireAuth(true); soapClient.setUsername("lichmama"); soapClient.setPassword("123456"); IUserService service = soapClient.create(IUserService.class); User user = service.getUserById(101); } }
最後交代一下開發環境
STS 3.7.3 + SpringBoot 2.0.1 + JDK1.8
收工下班了。