Technology Sharing

Spring Boot integrated rmi quick start demo

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. What is RMI?

RMI (Remote Method Invocation) is a basic idea in distributed programming. There are many technologies for implementing remote method invocation, such as CORBA and WebService, both of which are independent of each programming language. Java RMI is a remote method invocation mechanism designed specifically for the Java environment. It is a Java API for implementing remote procedures (RPC), which can directly transmit serialized Java objects and distributed garbage collection. Its implementation depends on the JVM, so it supports calls from one JVM to another. In Java RMI, the remote server implements specific Java methods and provides interfaces. The local client only needs to provide corresponding parameters according to the definition of the interface class to call the remote method, where the object is encoded and transmitted in a serialized manner. So the exploitation of deserialization vulnerabilities often involves RMI, which is what it means. The communication protocol that RMI relies on is JRMP (Java Remote Message Protocol), which is customized for Java and requires that both the server and the client must be written in Java.

Interaction process

rmi_architecture

registry

The interaction process can be simply summarized as:

  1. First, start the RMI Registry service. You can specify the port that the service listens on or use the default port (1099).
  2. Secondly, the server first instantiates an implementation class that provides services locally, and then registers the instantiated implementation class to the RMI Registry through the bind or rebind method of the Naming/Context/Registry class provided by RMI and exposes a name to the outside world;
  3. Finally, the client uses the local interface and a known name (name exposed by the RMI Registry) to get the implementation class from the RMI Service using the lookup methods of the Naming/Context/Registry classes provided by RMI. In this way, although there is no implementation class of this class locally, all methods are in the interface, so the methods of the remote object can be called;

2. Code Engineering

Experimental objectives

Experiment with a simple rmi service and call it through the client

rmi-server

This is a server-side project, mainly providing rmi service interface

pom.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>rmi</artifactId>
  7. <groupId>com.et</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>rmi-server</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-web</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-autoconfigure</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-test</artifactId>
  28. <scope>test</scope>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-data-jpa</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>com.et</groupId>
  36. <artifactId>rmi-common</artifactId>
  37. <version>1.0-SNAPSHOT</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>com.h2database</groupId>
  41. <artifactId>h2</artifactId>
  42. <version>1.4.200</version>
  43. </dependency>
  44. </dependencies>
  45. </project>
config
  1. package com.et.rmi.server.config;
  2. import com.et.rmi.server.dao.CustomerRepository;
  3. import com.et.rmi.server.model.Customer;
  4. import com.et.rmi.server.service.CustomerServiceImpl;
  5. import om.et.rmi.common.CustomerService;
  6. import org.springframework.boot.ApplicationArguments;
  7. import org.springframework.boot.ApplicationRunner;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.remoting.rmi.RmiServiceExporter;
  10. import org.springframework.stereotype.Component;
  11. import java.util.logging.Logger;
  12. @Component
  13. public class RmiServerApplicationRunner implements ApplicationRunner {
  14. private CustomerRepository repository;
  15. private CustomerServiceImpl customerService;
  16. private final Logger log = Logger.getLogger(this.getClass().getName());
  17. public RmiServerApplicationRunner(CustomerServiceImpl customerService) {
  18. this.customerService = customerService;
  19. }
  20. @Override
  21. public void run(ApplicationArguments args) throws Exception {
  22. Customer customer1 = new Customer("John", "Smith", "123-456-7890");
  23. customerService.saveCustomer(customer1);
  24. customerService.getCustomers().forEach(System.out::println);
  25. }
  26. @Bean
  27. public RmiServiceExporter customerServiceExporter() {
  28. RmiServiceExporter customerServiceExporter = new RmiServiceExporter();
  29. customerServiceExporter.setRegistryPort(1199);
  30. customerServiceExporter.setServiceName("customerService");
  31. customerServiceExporter.setServiceInterface(CustomerService.class);
  32. customerServiceExporter.setService(customerService);
  33. log.info("Started RMI Server");
  34. return customerServiceExporter;
  35. }
  36. }
service
  1. package com.et.rmi.server.service;
  2. import com.et.rmi.server.dao.CustomerRepository;
  3. import com.et.rmi.server.mapper.CustomerMapper;
  4. import com.et.rmi.server.model.Customer;
  5. import om.et.rmi.common.CustomerDTO;
  6. import om.et.rmi.common.CustomerService;
  7. import org.springframework.stereotype.Service;
  8. import java.util.List;
  9. @Service
  10. public class CustomerServiceImpl implements CustomerService {
  11. private CustomerRepository repository;
  12. public CustomerServiceImpl(CustomerRepository repository) {
  13. this.repository = repository;
  14. }
  15. @Override
  16. public CustomerDTO getCustomer(long id) {
  17. Customer customer = repository.findById(id).orElseThrow(IllegalArgumentException::new);
  18. CustomerMapper mapper = new CustomerMapper();
  19. CustomerDTO dto = mapper.mapToDTO(customer);
  20. System.out.println(dto);
  21. return dto;
  22. }
  23. public List<Customer> getCustomers() {
  24. return (List<Customer>)repository.findAll();
  25. }
  26. public void saveCustomer(Customer customer) {
  27. repository.save(customer);
  28. }
  29. }
dao
  1. package com.et.rmi.server.dao;
  2. import com.et.rmi.server.model.Customer;
  3. import org.springframework.data.repository.CrudRepository;
  4. import org.springframework.stereotype.Repository;
  5. import java.util.Optional;
  6. @Repository
  7. public interface CustomerRepository extends CrudRepository<Customer, Long> {
  8. Optional<Customer> findById(long id);
  9. }
mapper
  1. package com.et.rmi.server.mapper;
  2. import com.et.rmi.server.model.Customer;
  3. import om.et.rmi.common.CustomerDTO;
  4. public class CustomerMapper {
  5. public CustomerMapper() {
  6. }
  7. public CustomerDTO mapToDTO(Customer customer){
  8. CustomerDTO dto = new CustomerDTO();
  9. dto.setFirstName(customer.getFirstName());
  10. dto.setLastName(customer.getLastName());
  11. dto.setSocialSecurityCode(customer.getSocialSecurityCode());
  12. return dto;
  13. }
  14. }
model
  1. package com.et.rmi.server.model;
  2. import javax.persistence.*;
  3. @Entity
  4. @SequenceGenerator(name = "CUST_SEQ", initialValue = 1_000_001)
  5. public class Customer {
  6. @Id
  7. @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUST_SEQ")
  8. private long id;
  9. private String firstName;
  10. private String lastName;
  11. private String socialSecurityCode;
  12. public Customer() {
  13. }
  14. public Customer(String firstName, String lastName, String socialSecurityCode) {
  15. this.firstName = firstName;
  16. this.lastName = lastName;
  17. this.socialSecurityCode = socialSecurityCode;
  18. }
  19. public long getId() {
  20. return id;
  21. }
  22. public String getFirstName() {
  23. return firstName;
  24. }
  25. public void setFirstName(String firstName) {
  26. this.firstName = firstName;
  27. }
  28. public String getLastName() {
  29. return lastName;
  30. }
  31. public void setLastName(String lastName) {
  32. this.lastName = lastName;
  33. }
  34. public String getSocialSecurityCode() {
  35. return socialSecurityCode;
  36. }
  37. public void setSocialSecurityCode(String socialSecurityCode) {
  38. this.socialSecurityCode = socialSecurityCode;
  39. }
  40. @Override
  41. public String toString() {
  42. return "Customer{" +
  43. "id=" + id +
  44. ", firstName='" + firstName + ''' +
  45. ", lastName='" + lastName + ''' +
  46. ", socialSecurityCode='" + socialSecurityCode + ''' +
  47. '}';
  48. }
  49. }
application.properties
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

rmi-client

This is a client project, mainly calling the remote rmi service

pom.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>rmi</artifactId>
  7. <groupId>com.et</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>rmi-cilent</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-web</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-autoconfigure</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-test</artifactId>
  28. <scope>test</scope>
  29. </dependency>
  30. <dependency>
  31. <groupId>com.et</groupId>
  32. <artifactId>rmi-common</artifactId>
  33. <version>1.0-SNAPSHOT</version>
  34. </dependency>
  35. </dependencies>
  36. </project>
controller
  1. package com.et.rmi.client.controller;
  2. import om.et.rmi.common.CustomerDTO;
  3. import om.et.rmi.common.CustomerService;
  4. import org.springframework.http.MediaType;
  5. import org.springframework.http.ResponseEntity;
  6. import org.springframework.remoting.rmi.RmiProxyFactoryBean;
  7. import org.springframework.stereotype.Controller;
  8. import org.springframework.web.bind.annotation.PathVariable;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RequestMethod;
  11. @Controller
  12. @RequestMapping(value = "customers")
  13. public class CustomerController {
  14. private RmiProxyFactoryBean proxyFactoryBean;
  15. public CustomerController(RmiProxyFactoryBean proxyFactoryBean) {
  16. this.proxyFactoryBean = proxyFactoryBean;
  17. }
  18. @RequestMapping(value = "{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
  19. public ResponseEntity<CustomerDTO> getCustomer(@PathVariable long id) {
  20. CustomerService service = (CustomerService) proxyFactoryBean.getObject();
  21. CustomerDTO dto = service.getCustomer(id);
  22. return ResponseEntity.ok(dto);
  23. }
  24. }
config
  1. package com.et.rmi.client.config;
  2. import om.et.rmi.common.CustomerService;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.remoting.rmi.RmiProxyFactoryBean;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.util.StringUtils;
  7. import java.util.logging.Logger;
  8. @Component
  9. public class Config {
  10. public final Logger log = Logger.getLogger(this.getClass().getName());
  11. @Bean
  12. public RmiProxyFactoryBean proxyFactoryBean() {
  13. String remoteHost = System.getProperty("RMI_SERVER_HOST");
  14. if(StringUtils.isEmpty(remoteHost)){
  15. remoteHost="127.0.0.1";
  16. }
  17. String rmiHost = String.format("rmi://%s:1199/customerService", remoteHost);
  18. log.info("RMI Host name is " + rmiHost);
  19. RmiProxyFactoryBean proxy = new RmiProxyFactoryBean();
  20. proxy.setServiceInterface(CustomerService.class);
  21. proxy.setServiceUrl(rmiHost);
  22. proxy.afterPropertiesSet();
  23. return proxy;
  24. }
  25. }
application.properties
server.port=8081

rmi-common

This is a public package, both server and client must reference it

  1. package om.et.rmi.common;
  2. import java.io.Serializable;
  3. public class CustomerDTO implements Serializable {
  4. private String firstName;
  5. private String lastName;
  6. private String socialSecurityCode;
  7. public CustomerDTO() {
  8. }
  9. public String getFirstName() {
  10. return firstName;
  11. }
  12. public void setFirstName(String firstName) {
  13. this.firstName = firstName;
  14. }
  15. public String getLastName() {
  16. return lastName;
  17. }
  18. public void setLastName(String lastName) {
  19. this.lastName = lastName;
  20. }
  21. public String getSocialSecurityCode() {
  22. return socialSecurityCode;
  23. }
  24. public void setSocialSecurityCode(String socialSecurityCode) {
  25. this.socialSecurityCode = socialSecurityCode;
  26. }
  27. @Override
  28. public String toString() {
  29. final StringBuffer sb = new StringBuffer("CustomerDTO{");
  30. sb.append("firstName='").append(firstName).append(''');
  31. sb.append(", lastName='").append(lastName).append(''');
  32. sb.append(", socialSecurityCode='").append(socialSecurityCode).append(''');
  33. sb.append('}');
  34. return sb.toString();
  35. }
  36. }
  1. package om.et.rmi.common;
  2. public interface CustomerService {
  3. CustomerDTO getCustomer(long id);
  4. }

The above are just some key codes. For all codes, please refer to the following code repository

Code repository

3. Testing

  • Start the rmi-server service
  • Start the rmi-client service
  • Visit http://127.0.0.1:8081/customers/1000001
  • return{"firstName":"John","lastName":"Smith","socialSecurityCode":"123-456-7890"}

4. References