DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • Building REST API Backend Easily With Ballerina Language
  • Visually Designing Views for Java Web Apps

Trending

  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • A Complete Guide to Modern AI Developer Tools
  • Why Documentation Matters More Than You Think
  • Subtitles: The Good, the Bad, and the Resource-Heavy
  1. DZone
  2. Data Engineering
  3. Databases
  4. How To Build Web Service Using Spring Boot 2.x

How To Build Web Service Using Spring Boot 2.x

Do you want to create a web-service application using Spring Boot? Check out this architecture template and let it be a kick for your starting.

By 
Vishnu Viswambharan user avatar
Vishnu Viswambharan
·
Updated Feb. 07, 22 · Tutorial
Likes (12)
Comment
Save
Tweet
Share
8.7K Views

Join the DZone community and get the full member experience.

Join For Free

Architecture Contains

  • MVC Architecture
  • JWT Based Authentication
  • Spring Data (JPA)
  • Application User Password Encryption
  • DB password Encryption.
  • SQL Server
  • Slf4j
  • Swagger For API Doc

Repository Contains

  • Application Source code
  • SQL script of Data Base along with key data
  • DB.txt file contains the DB config details.
  • Postman JSON script to test all web services.

Steps to Run Applications

  • Install JDK 11 or the latest version.
  • Clone the Project repository into local.
  • Git Url: https://github.com/VishnuViswam/sample-web-service.git
  • Install SQL server 2012.
  • Create application DB and user
  • Insert the DB key data.
  • Add the decoding key of the database password into the system variables. It is present in the DB.txt file.
  • Sometimes we may need to restart the windows to pick up the updated system variables.
  • Run the project source code.
  • To call the web services, import provided postman JSON scripts into the postman client application.

About Project Configurations

Web-Service Declaration

Each Web-services of the application will be declared in the controller layer.

Example

Java
 
@RequestMapping("/api/v1/user")
@RestController
@Validated
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private GeneralServices generalServices;

    @Autowired
    private UserService userService;

    /**
     * Web service to create new user
     *
     * @param httpServletRequest
     * @param user
     * @return
     */
    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> createUser(HttpServletRequest httpServletRequest,
                                             @Valid @RequestBody UserCreateModel user) {
        logger.debug("<--- Service to save new user request : received --->");
        ApiSuccessResponse apiResponse = userService.createUser(user, generalServices.getApiRequestedUserId(httpServletRequest));
        logger.debug("<--- Service to save new user response : given --->");
        return ResponseEntity.status(HttpStatus.CREATED).body(apiResponse);

    }

}

  • @RequestMapping("/api/v1/user") annotation is used to mention the category of web service.
  • @RestController annotation will configure the class to receive the rest-full web service call.
  • @PostMapping() annotation will decide the HTTP request type.
  • consumes & consumes tags will decide the content type of the HTTP request and response.

From this "controller layer," API requests will be taken to the service layer. All business logic will be handled here, then it will talk with the database using JPA.

Common Error Handling

Whenever an exception happens, it will throw from the respective classes and be handled in the "CommonExceptionHandlingController." We have to handle this separately for each type of exception. This function is performed with the help of "ControllerAdvice" named annotation.

Example

Java
 
@ControllerAdvice
public class CommonExceptionHandlingController extends ResponseEntityExceptionHandler {

    private static final Logger logger = 
      				LoggerFactory.getLogger(CommonExceptionHandlingController.class);

    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException httpRequestMethodNotSupportedException,
                                                                         HttpHeaders headers, HttpStatus status, WebRequest request) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ApiErrorResponse(Constants.WRONG_HTTP_METHOD,
                Constants.WRONG_HTTP_METHOD_ERROR_MESSAGE, Calendar.getInstance().getTimeInMillis()));
    }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException methodArgumentNotValidException,
                                                                  HttpHeaders headers, HttpStatus status, WebRequest request) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ApiErrorResponse(Constants.MANDATORY_FIELDS_ARE_NOT_PRESENT_CODE,
                Constants.MANDATORY_FIELDS_ARE_NOT_PRESENT_ERROR_MESSAGE, Calendar.getInstance().getTimeInMillis()));
    }

--------
--------

Spring Data (JPA) Configuration

  • All interaction of the application with the database will handle by the JPA library.
  • JPA will have an Entity class and a corresponding Repository interface for all logical objects in the application.

Entity Class

Java
 




xxxxxxxxxx
1
17


 
1
@Entity
2
@Table(name = "tbl_users")
3
public class Users implements Serializable {
4

          
5
    private static final long serialVersionUID = 1L;
6

          
7
    @Id
8
    @GeneratedValue(strategy = GenerationType.IDENTITY)
9
    @Column(name = "id", columnDefinition = "bigint")
10
    private Long id;
11

          
12
    @OneToOne(fetch = FetchType.EAGER)
13
    @JoinColumn(name = "user_account_id", columnDefinition = "bigint", nullable = false)
14
    private UserAccounts userAccount;
15

          
16
--------
17
--------


Repository Interface

Java
 




xxxxxxxxxx
1
11


 
1
public interface UserRepository extends JpaRepository<Users, Long> {
2

          
3
    /**
4
     * To find user object using username
5
     *
6
     * @param username
7
     * @return
8
     */
9
    Users findByUserAccountUsername(String username);
10
---------
11
---------


  • Other JPA configurations will be done in application.properties named file.

JPA Database Configuration in Application Properties

Properties files
 




xxxxxxxxxx
1
11


 
1
spring.jpa.show-sql=false
2
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
3
spring.jpa.hibernate.ddl-auto = update
4

          
5
spring.jpa.properties.hibernate.show_sql=false
6
spring.jpa.properties.hibernate.format_sql=false
7
spring.jpa.properties.hibernate.use_sql=true
8
spring.jpa.open-in-view=false
9
spring.jpa.properties.hibernate.hbm2ddl.auto=update
10
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
11
spring.jpa.hibernate.connection.provider_class=org.hibernate.hikaricp.internal.HikariCPConnectionProvider


Database Configuration

  • The database name will be present in the application.properties file.
  • Other information like connection URL and user credentials will be mentioned in two different other property files.
    • application-dev.properties
      • It will have the configurations which we used for the development.
    • application-pro.properties
      • It will have the configurations which we used for the production.
spring.profiles.active=dev
  • The above-mentioned property configuration will be present in the main "application.properties" file.
  • It will decide which sub-property file should load to the system(dev or pro).

application.properties

Properties files
 
#DB config
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

application-dev.properties

Properties files
 
#DB config
spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=sample_webservice_db_dev
spring.datasource.username=dbuser
spring.datasource.password=ENC(tZTfehMYyz4EO0F0uY8fZItE7K35RtkA)
#spring.datasource.username=dbuser
#spring.datasource.password=dbuserpassword

application-pro.properties

Properties files
 
#DB config
spring.datasource.url=jdbc:sqlserver://192.168.1.119:1433;databaseName=sample_webservice_db
spring.datasource.username=proUser
spring.datasource.password=ENC(proUserPswd)

Database Password Encryption

  • The application database password will be encrypted using __Jasypt __ library with the help of an encryption key.
  • This encryption key needs to add to the computer system variables of environmental variables under the "JASYPT_ENCRYPTOR_PASSWORD" named key.
  • We have to mention the encrypted database password in the property file as follows. This is how the system will understand the password needs to be decrypted using a secret key which is added in the system variables.
Properties files
 
spring.datasource.password=ENC(tZTfehMYyz4EO0F0uY8fZItE7K35RtkA)
  • For the__Jasypt __  decryption we need to mention the default encryption configuration in the property file as follows:
 
jasypt.encryptor.algorithm=PBEWithMD5AndDES
jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator
  • We also provide @EnableEncryptableProperties annotation in the application main class to let the application know about this database password encryption configuration.

SampleWebservice.java

Java
 




xxxxxxxxxx
1


 
1
@SpringBootApplication
2
@EnableEncryptableProperties
3
public class SampleWebservice extends SpringBootServletInitializer {
4
--------
5
--------


JWT Authentication Configuration

  • We implemented JSON Web Token-based authentication with the help of spring security.
  • Upon the success of a logged-in user, we will create two tokens (accessToken & refreshToken) and send them back to the client.
  • accessToken will be created using a private key, expiry time (1 hr), user id, and role name.
  • refreshToken will be created using a private key, expiry time (24 hr), user id, and role name.
  • After successful login, each API request needs to have this accessToken in the header under the Authorization key.
  • A "bearer" named key should be attached at the starting of the access token like follows.
  • "bearer accessToken"
  • The access token will keep monitor in every web-service request.
  • If the validity of the access token expires we revert the request with 401 HTTP status.
  • At that moment web-service user (client) needs to call access token renewal request using the refresh token.
  • Then we will check the validity of the refresh token. If it is not expired we will give a new access token and refresh token.
  • The client can continue using these new tokens.
  • If the validity of the refresh token also expired, we ask them to re-login using their username and password.

Process of Creating Tokens

UnAuthorisedAccessServiceImpl.java

Java
 

@Override
    public ApiSuccessResponse userLoginService(String username, String password) {
        Tokens tokens = null;
        Users user = userService.findByUsername(username);
        if (user != null) {
            if (passwordEncryptingService.matches(password,
                    user.getUserAccount().getPassword())) {
                if (user.getUserAccount().getStatus() == Constants.ACTIVE_STATUS) {
                    String roleName = user.getUserAccount().getUserRole().getRoleName();
                    // Creating new tokens
                    try {
                        tokens = createTokens(user.getUserAccount().getId().toString(), roleName);
                    } catch (Exception exception) {
                        logger.error("Token creation failed : ", exception);
                        throw new UnknownException();
                    }

                    // Validating tokens
                    if (validationService.validateTokens(tokens)) {
                        tokens.setUserId(user.getUserAccount().getId());
                        return new ApiSuccessResponse(tokens);

                    } else {
                        throw new UnknownException();
                    }

                } else {
                    return new ApiSuccessResponse(new ApiResponseWithCode(Constants.USER_ACCOUNT_IS_INACTIVE_ERROR_CODE,
                            Constants.USER_ACCOUNT_IS_INACTIVE_ERROR_MESSAGE));
                }

            } else {
                return new ApiSuccessResponse(new ApiResponseWithCode(Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_CODE,
                        Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_MESSAGE));
            }

        } else {
            return new ApiSuccessResponse(new ApiResponseWithCode(Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_CODE,
                    Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_MESSAGE));
        }
    }

    @Override
    public ApiSuccessResponse createNewAccessTokenUsingRefreshToken(String refreshToken) {
        Tokens tokens = null;
        UserAccounts userAccount = null;
        AppConfigSettings configSettings = appConfigSettingsService.findByConfigKeyAndStatus(Constants.JWT_SECRET_KEY,
                Constants.ACTIVE_STATUS);
        // Validate Refresh token
        userAccount = jwtTokenHandler.validate(configSettings.getConfigValue(), refreshToken);
        if (userAccount != null) {
            // Creating new tokens if provided refresh token is valid
            try {
                tokens = createTokens(userAccount.getId().toString(), userAccount.getRole());
            } catch (Exception exception) {
                logger.error("Token creation failed : ", exception);
                throw new UnknownException();
            }
            if (validationService.validateTokens(tokens)) {
                tokens.setUserId(userAccount.getId());
                return new ApiSuccessResponse(tokens);

            } else {
                throw new UnknownException();
            }
        } else {
            return new ApiSuccessResponse(new ApiResponseWithCode(Constants.REFRESH_TOKEN_EXPIRED_ERROR_CODE,
                    Constants.REFRESH_TOKEN_EXPIRED_ERROR_MESSAGE));
        }
    }
  • In the above code userLoginService named method will check the credentials of the user and provide tokens if it is valid.
  • CreateNewAccessTokenUsingRefreshToken named method will create the new access token and refresh token upon the success refresh token validation.

Process of Filtering and Validating Tokens

WebConfig.java

Java
 




xxxxxxxxxx
1
36


 
1
@Configuration
2
@EnableWebSecurity
3
@EnableGlobalMethodSecurity(prePostEnabled = true)
4
public class WebConfig extends WebSecurityConfigurerAdapter {
5

          
6
    @Autowired
7
    private JwtAuthenticationProvider authenticationProvider;
8

          
9
    @Autowired
10
    private JwtAuthenticationEntryPoint entryPoint;
11

          
12
    @Bean
13
    public AuthenticationManager authenticationManager() {
14
        return new ProviderManager(Collections.singletonList(authenticationProvider));
15
    }
16

          
17
    @Bean
18
    public JwtAuthenticationTokenFilter authenticationTokenFilter() {
19
        JwtAuthenticationTokenFilter filter = new JwtAuthenticationTokenFilter();
20
        filter.setAuthenticationManager(authenticationManager());
21
        filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());
22
        return filter;
23
    }
24

          
25
    @Override
26
    protected void configure(HttpSecurity http) throws Exception {
27
        http.csrf().disable()
28
                .exceptionHandling().authenticationEntryPoint(entryPoint).and().sessionManagement()
29
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
30
                .addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class)
31
                .addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
32
                .headers().cacheControl();
33

          
34
    }
35

          
36
}


  • This configuration will enable the spring security module using @EnableWebSecurity AND @EnableGlobalMethodSecurity(prePostEnabled = true) named annotations.
  • Here we will inject the JWT filter into the HTTP request of the system.

JwtAuthenticationTokenFilter.java

Java
 
public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private GeneralServices generalServices;

    public JwtAuthenticationTokenFilter() {
        super("/api/**");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
                                                HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
                                                  -------
                                                  --------
    }
  • Here, in the above class JwtAuthenticationTokenFilter() named method will filter all incoming web-service requests who have the "api" named keyword in the URL.
  • All filtered web-service requests will reach the attemptAuthentication named method.
  • We can do all our business logic in this method.

Application User Password Encryption

  • All passwords of the users in this application will be encrypted for security using BCrypt.

PasswordEncryptingService.java

Java
 




xxxxxxxxxx
1


 
1
public class PasswordEncryptingService {
2

          
3
    public String encode(CharSequence rawPassword) {
4
        return BCrypt.hashpw(rawPassword.toString(), BCrypt.gensalt(6));
5
    }
6

          
7
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
8
        return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
9
    }


  • Here, the encode named method is used to encrypt the password.
  • matches named method is used to cross-check the provided password and actual password of the user.

Log Configuration Using Slf4j

  • We have one XML file to configure the Log named by logback-spring.xml.
  • To log information from each class, we need to inject the respective class to Slf4j.

Example

UserServiceImpl.java

Java
 
@Service("UserService")
@Scope("prototype")
public class UserServiceImpl implements UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
  • The above code snippet shows how we inject the class into the logger.
  • Following are the basic methods to log the information.
    • logger.error("Error");
    • logger.info("Info");
    • logger.warn("Warn");

Swagger For API Doc

  • API doc has an important role in the web-service application.
  • Previously we used to create API doc using any static Excel documents.
  • This library will help us to create the API doc using some annotations inside the application.

Pom.xml

XML
 
         <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>${springfox.swagger.version}</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox.swagger.version}</version>
        </dependency>
  • These are the libraries we used in the pom file to integrate Swagger.
  • We need to do some configurations in the applications to enable the API doc.

SwaggerAPIDocConfig.java

Java
 




xxxxxxxxxx
1
34


 
1
@Configuration
2
@EnableSwagger2
3
public class SwaggerAPIDocConfig {
4

          
5

          
6
    public static final Contact DEFAULT_CONTACT = new Contact("Demo", "http://www.demo.ae/",
7
            "[email protected]");
8

          
9
    public static final ApiInfo DEFAUL_API_INFO = new ApiInfo("Sample Application",
10
            "Sample Application description.",
11
            "1.0.0",
12
            "http://www.sampleapplication.ae/",
13
            DEFAULT_CONTACT, "Open licence",
14
            "http://www.sampleapplication.ae/#license",
15
            new ArrayList<VendorExtension>());
16

          
17
    private static final Set<String> DEFAULT_PRODICERS_AND_CONSUMERS =
18
            new HashSet<>(Arrays.asList("application/json", "application/xml"));
19

          
20

          
21
    @Bean
22
    public Docket api() {
23
        return new Docket(DocumentationType.SWAGGER_2)
24
                .apiInfo(DEFAUL_API_INFO)
25
                .produces(DEFAULT_PRODICERS_AND_CONSUMERS)
26
                .consumes(DEFAULT_PRODICERS_AND_CONSUMERS)
27
                .select()
28
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
29
                .paths(PathSelectors.any())
30
                .build();
31

          
32
    }
33

          
34
}


  • As we can see in the above class, we need to add some basic information about our project.
  • We need to tell Swagger from which class it needs to create API docs, and that is configured under .apis(RequestHandlerSelectors.withClassAnnotation,(RestController.class)) named line.
  • Swagger API doc will be accessible from http://localhost:8080/sampleWebService/apidoc.

Postman Script

  • We can find 2 Postman JSON scripts in the repository. Please import both of them into the Postman client application.
  • Execute the login web-service request at first. Then execute the rest of the web services.

Thank you!

Web Service Spring Framework application Database Spring Boot Java (programming language) Requests Property (programming) API

Opinions expressed by DZone contributors are their own.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • Building REST API Backend Easily With Ballerina Language
  • Visually Designing Views for Java Web Apps

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: