Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Securing REST APIs with Spring Security

Securing REST APIs is crucial to protect your application from unauthorized access. This guide covers key concepts and steps for securing REST APIs in your Spring Boot application, including adding dependencies, configuring security, and protecting endpoints.

Key Concepts of Securing REST APIs

  • REST API Security: Implementing measures to protect your API from unauthorized access.
  • JWT (JSON Web Token): A compact, URL-safe token used for secure data transmission.
  • Security Configuration: Configuring Spring Security to secure REST APIs.

Adding Dependencies

Include the Spring Security and JWT dependencies in your pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

Configuring JWT Security

Configure JWT security by creating utility classes for generating and validating JWT tokens:

Example: JwtUtil.java

// JwtUtil.java
package com.example.myapp.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {

    private String SECRET_KEY = "secret";

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public  T extractClaim(String token, Function claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public String generateToken(String username) {
        Map claims = new HashMap<>();
        return createToken(claims, username);
    }

    private String createToken(Map claims, String subject) {
        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
    }

    public Boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }
}

Creating JWT Filters

Create filters to intercept requests and validate JWT tokens:

Example: JwtRequestFilter.java

// JwtRequestFilter.java
package com.example.myapp.filter;

import com.example.myapp.service.MyUserDetailsService;
import com.example.myapp.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

Configuring Security

Configure security by extending WebSecurityConfigurerAdapter and overriding the configure(HttpSecurity http) method:

Example: SecurityConfiguration.java

// SecurityConfiguration.java
package com.example.myapp.config;

import com.example.myapp.filter.JwtRequestFilter;
import com.example.myapp.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests().antMatchers("/authenticate", "/register").permitAll()
            .anyRequest().authenticated()
            .and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

Generating JWT Tokens

Create a controller to authenticate users and generate JWT tokens:

Example: AuthController.java

// AuthController.java
package com.example.myapp.controller;

import com.example.myapp.service.MyUserDetailsService;
import com.example.myapp.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/authenticate")
    public String createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()));
        
        final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
        final String jwt = jwtUtil.generateToken(userDetails.getUsername());

        return jwt;
    }
}

Example: AuthenticationRequest.java

// AuthenticationRequest.java
package com.example.myapp.model;

public class AuthenticationRequest {
    private String username;
    private String password;

    // getters and setters
}

Securing Endpoints

Protect specific endpoints by specifying access rules:

Example: SecurityConfiguration.java

// SecurityConfiguration.java
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests().antMatchers("/authenticate", "/register").permitAll()
        .anyRequest().authenticated()
        .and().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}

Testing JWT Authentication

Use tools like Postman or curl to test your secured REST APIs:

Example: Testing with curl

$ curl -X POST -H "Content-Type: application/json" -d '{"username":"user", "password":"password"}' http://localhost:8080/authenticate
$ curl -H "Authorization: Bearer YOUR_JWT_TOKEN" http://localhost:8080/secured-endpoint

Key Points

  • REST API Security: Implementing measures to protect your API from unauthorized access.
  • JWT (JSON Web Token): A compact, URL-safe token used for secure data transmission.
  • Security Configuration: Configuring Spring Security to secure REST APIs.
  • Include the Spring Security and JWT dependencies in your pom.xml file.
  • Configure JWT security by creating utility classes for generating and validating JWT tokens.
  • Create filters to intercept requests and validate JWT tokens.
  • Configure security by extending WebSecurityConfigurerAdapter and overriding the configure(HttpSecurity http) method.
  • Create a controller to authenticate users and generate JWT tokens.
  • Protect specific endpoints by specifying access rules.
  • Use tools like Postman or curl to test your secured REST APIs.

Conclusion

Securing REST APIs with Spring Security and JWT ensures that your application is protected from unauthorized access. By understanding and implementing JWT security, security configuration, and authentication flows, you can enhance the security of your Spring Boot application. Happy coding!