“讲真,SOAP跟现在流行的RESTful WebService比起来显得很难用。冗余的XML文本信息太多,可读性差,它的请求信息有时很难手动构造,不太好调试。不过说归说,对某些企业用户来说SOAP的使用率仍然是很高的。
”
需求背景
接手维护的一个项目,最近客户想开放项目中的功能给第三方调用,而且接入方指定必须是SOAP接口。这项目原来的代码我看着头疼,也不想再改了,除非推倒重写。客户的需求虽然不难但要的很急,为了尽快交付就使用SpringBoot快速搭一个微服务。
开始动手
1. .新建一个Spring Starter Project
2. .加入cxf的maven配置
3. <dependency>
4. <groupId>org.apache.cxf</groupId>
5. <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
6. <version>3.1.</version>
7. </dependency>
编写服务代码(示例代码)
1. @WebService(targetNamespace="http://demo.example.com/")
2. public interface IUserService {
4. @WebMethod
5. User getUserById(@WebParam(name = "id") int id);
7. @WebMethod
8. int addUser(@WebParam(name = "user") User user);
9. }
1. @InInterceptors(interceptors={"com.example.demo.auth.AuthInterceptor"})
2. @WebService(serviceName = "UserService", targetNamespace = "http://demo.example.com/", endpointInterface = "com.example.demo.soap.IUserService")
3. @Component
4. public class UserServiceImpl implements IUserService {
6. private Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
8. @Autowired
9. private IUserDAO userDAO;
11. @Override
12. public User getUserById(int id) {
13. return userDAO.getUserById(id);
14. }
16. @Override
17. public int addUser(User user) {
18. logger.info("save user [" + user.getId() + "]");
19. userDAO.addUser(user);
20. return 0;
21. }
22. }
鉴权拦截器
1. public class AuthInterceptor extends AbstractSoapInterceptor {
3. private static final String BASIC_PREFIX = "Basic ";
4. private static final String USERNAME = "lichmama";
5. private static final String PASSWORD = "123456";
7. public AuthInterceptor() {
8. super(Phase.PRE_INVOKE);
9. }
11. @Override
12. public void handleMessage(SoapMessage message) throws Fault {
13. HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
14. String auth = request.getHeader("Authorization");
15. if (auth == null) {
16. SOAPException exception = new SOAPException("auth failed, header [Authorization] not exists");
17. throw new Fault(exception);
18. }
19. if (!auth.startsWith(BASIC_PREFIX)) {
20. SOAPException exception = new SOAPException("auth failed, header [Authorization] is illegal");
21. throw new Fault(exception);
22. }
23. String plaintext = new String(Base64.getDecoder().decode(auth.substring(BASIC_PREFIX.length())));
24. if (StringUtils.isEmpty(plaintext) || !plaintext.contains(":")) {
25. SOAPException exception = new SOAPException("auth failed, header [Authorization] is illegal");
26. throw new Fault(exception);
27. }
28. String[] userAndPass = plaintext.split(":");
29. String username = userAndPass[0];
30. String password = userAndPass[1];
31. if (!USERNAME.equals(username) || !PASSWORD.equals(password)) {
32. SOAPException exception = new SOAPException("auth failed, username or password is incorrect");
33. throw new Fault(exception);
34. }
35. }
36. }
编写配置类
1. @Configuration
2. public class SoapConfig {
4. @Autowired
5. private IUserService userService;
7. @Autowired
8. @Qualifier(Bus.DEFAULT_BUS_ID)
9. private SpringBus bus;
11. @Bean
12. public Endpoint endpoint() {
13. EndpointImpl endpoint = new EndpointImpl(bus, userService);
14. endpoint.publish("/userService");
15. return endpoint;
16. }
17. }
修改 CXF 默认发布路径(application.properties)
1. server.port=8000
2. cxf.path=/soap
启动项目后访问 http://localhost:8000/soap/userService?wsdl
使用 SoapUI 测试一下,看上去没什么问题
客户端增加对 Basic Auth 的支持:
1. /*
2. 使用Eclipse自动生成Web Service Client,在SoapBingdingStub的createCall()方法中加入一下代码:
3. */
5. // basic auth
6. _call.setProperty("javax.xml.rpc.security.auth.username", "lichmama");
7. _call.setProperty("javax.xml.rpc.security.auth.password", "123456");
然后正常调用即可,这里提供一下自己写的 SoapClient:
1. public class GeneralSoapClient {
3. private String WSDL;
5. private String namespaceURI;
7. private String localPart;
9. public GeneralSoapClient() {
11. }
13. public GeneralSoapClient(String WSDL, String namespaceURI, String localPart) {
14. this.WSDL = WSDL;
15. this.namespaceURI = namespaceURI;
16. this.localPart = localPart;
17. }
19. public String getWSDL() {
20. return WSDL;
21. }
23. public void setWSDL(String WSDL) {
24. this.WSDL = WSDL;
25. }
27. public String getNamespaceURI() {
28. return namespaceURI;
29. }
31. public void setNamespaceURI(String namespaceURI) {
32. this.namespaceURI = namespaceURI;
33. }
35. public String getLocalPart() {
36. return localPart;
37. }
39. public void setLocalPart(String localPart) {
40. this.localPart = localPart;
41. }
43. private boolean requireAuth = false;
45. private String username;
47. private String password;
49. public boolean isRequireAuth() {
50. return requireAuth;
51. }
53. public void setRequireAuth(boolean requireAuth) {
54. this.requireAuth = requireAuth;
55. }
57. public String getUsername() {
58. return username;
59. }
61. public void setUsername(String username) {
62. this.username = username;
63. }
65. public String getPassword() {
66. return password;
67. }
69. public void setPassword(String password) {
70. this.password = password;
71. }
73. /**
74. * 创建Soap Service实例
75. * @param serviceInterface
76. * @return
77. * @throws Exception
78. */
79. public <T> T create(Class<T> serviceInterface) throws Exception {
80. URL url = new URL(WSDL);
81. QName qname = new QName(namespaceURI, localPart);
82. Service service = Service.create(url, qname);
83. T port = service.getPort(serviceInterface);
84. if (requireAuth) {
85. BindingProvider prov = (BindingProvider) port;
86. prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
87. prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
88. }
89. return port;
90. }
91. }
1. public class TestSoap {
3. public static void main(String[] args) throws Exception {
4. String wsdl = "http://localhost:8080/soap/userService?wsdl";
5. String namespaceURI = "http://demo.example.com/";
6. String localPart = "UserService";
7. GeneralSoapClient soapClient = new GeneralSoapClient(wsdl, namespaceURI, localPart);
8. soapClient.setRequireAuth(true);
9. soapClient.setUsername("lichmama");
10. soapClient.setPassword("123456");
11. IUserService service = soapClient.create(IUserService.class);
12. User user = service.getUserById(101);
13. }
14. }
最后交代一下开发环境
1. STS 3.7.3 + SpringBoot 2.0.1 + JDK1.8
收工下班了。
作者:lichmama
来源链接:
https://www.bbsmax.com/A/gVdn0qppJW/