logo le blog invivoo blanc

Secure your application with Spring Security

17 October 2018 | Java | 0 comments

SPRING SECURITY DEFINITION

Spring Security is a lightweight security framework that provides authentication and authorization support to secure Spring applications. It comes with implementations of popular security algorithms.
This article guides us through the process of creating a simple connection example to an application with Spring Boot, Spring Security, Spring Data JPA and MYSQL.

APPLICATION CONFIGURATION

Let’s begin with a very basic application (in terms of installation requirements) that starts a Spring application context. Two tools that will help us with this are Maven and Spring Boot. I will be skipping lines that are not particularly interesting like the Maven repository configuration. You can find the complete code on GitHub.
To start off we will add the following dependency to the pom of our application.

1.  <dependency>
2.    <groupId>org.springframework.boot</groupId>
3.    <artifactId>spring-boot-starter-security</artifactId>
4.  </dependency>

At the start of our application we can see the following information in the console:

 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.4.RELEASE)
2018-09-10 22:44:09.059  INFO 645 --- [           main] .s.s.UserDetailsServiceAutoConfiguration : 
Using generated security password: c657aef6-758a-409d-ac02-814ff4df55be

Spring Boot therefore provides us with a default password.

We have just added Spring Security to our application’s classpath, Spring sets a default configuration, and asks a username and a password to access our application.
So to access our application with the Spring default configuration, we enter the username as username and the default password provided by Spring, displayed in the console at the start of our application (here c657aef6-758a-409d-ac02-814ff4df55be) in the authentication form.

Spring Boot 1

In the rest of this article, we will be customizing this configuration.

CREATING USER ENTITY

We are going to create the User.java class, this class implements UserDetails interface.
The interface provides information about the primary user. Implementations are not used directly by Spring Security in a matter of security purposes. They simply store user information that is then encapsulated in authentication objects. This makes it possible to store non-security related information (such as e-mail addresses, etc.) in an appropriate location.

@Entity
@Table(name = "USER")
public class User implements Serializable , UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer userId;
    private String username;
    private String password;
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUsername() {
        return username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return false;
    }
    @Override
    public boolean isAccountNonLocked() {
        return false;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }
    @Override
    public boolean isEnabled() {
        return false;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

Below are the feed scripts from our table in the H2 embedded database. The script is placed in the data.sql file, the scripts located in the data.sql file are executed each time we start our application.

INSERT INTO USER(username, password) VALUES('ADMIN', 'ADMIN')

IMPLEMENTATION OF APPENHENTICATIONPROVIDER CLASS

Spring Security provides a variety of options in order to perform authentication. All of these options follow a simple contract. AuthenticationProvider processes an authentication request and a fully authenticated object with full credentials is returned.
The standard and most common implementation is the DaoAuthenticationProvider – which retrieves user details from a simple read-only DAO user – the UserDetailsService. This detail user service can only access the user name to retrieve the complete user entity and in most cases, this is sufficient.

public class AppAuthProvider extends DaoAuthenticationProvider {
    @Autowired
    UserService userDetailsService;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
        String name = auth.getName();
        String password = auth.getCredentials()
                .toString();
        UserDetails user = userDetailsService.loadUserByUsername(name);
        if (user == null) {
            throw new BadCredentialsException("Username/Password does not match for " + auth.getPrincipal());
        }
        return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
}

IMPLEMENTATION OF THE REPOSITORY USERREPOSITORY

We are going to create a UserRepository.java class, this class inherits the JpaRepository class. The function findUserWithName (String name) retrieves a User by his username.

public interface UserRepository extends JpaRepository<User, Integer> {
    @Query(" select u from User u " +
            " where u.username = ?1")
    Optional<User> findUserWithName(String username);
}

IMPLEMENTATION OF THE USERSERVICE SERVICE

We are going to create the UserService.java class, this class implements a UserDetailsService interface. The UserDetailsService interface is used to retrieve the data related to the user. It uses a method called loadUserByUsername that finds a user based on the user name and can be overridden to customize the user’s search process. It is used by DaoAuthenticationProvider and loads details about the user during authentication.

@Service
@Slf4j
public class UserService implements UserDetailsService {
    private final UserRepository userRepository;
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Objects.requireNonNull(username);
        User user = userRepository.findUserWithName(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        return user;
    }
}

IMPLEMENTATION OF SECURITYCONFIG CONFIGURATION CLASS

To customize the configuration we will create a class that inherits from WebSecurityConfigurerAdaptater. This class must have @EnableWebSecurity and @Configuration notes. Configuration classes are scanned when the application starts.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userDetailsService;
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(new Http403ForbiddenEntryPoint() {
                })
                .and()
                .authenticationProvider(getProvider())
                .formLogin()
                .loginProcessingUrl("/login")
                .successHandler(new AuthentificationLoginSuccessHandler())
                .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new AuthentificationLogoutSuccessHandler())
                .invalidateHttpSession(true)
                .and()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/logout").permitAll()
                .antMatchers("/user").authenticated()
                .anyRequest().permitAll();
    }
    private class AuthentificationLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException {
            response.setStatus(HttpServletResponse.SC_OK);
        }
    }
    private class AuthentificationLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                    Authentication authentication) throws IOException, ServletException {
            response.setStatus(HttpServletResponse.SC_OK);
        }
    }
    @Bean
    public AuthenticationProvider getProvider() {
        AppAuthProvider provider = new AppAuthProvider();
        provider.setUserDetailsService(userDetailsService);
        return provider;
    }
}

We define how users are managed in the configure(AuthenticationManagerBuilder auth) method. Here the user management is dynamic, we manage users via the service UserSevice.
Protected resources’ management is done on the level of the configure(HttpSecurityhttp) function. Any access to url / user / ** needs to be authenticated. The authentication is done via a request of type Post to url / login.

POSTMAN TESTS

We are going to test the access to our application using Postman Rest, which is a API web client.
By accessing GET: / user / we have an authentication error as below:

spring boot 2

Access to GET: / user / needs to be authenticated beforehand.
To authenticate it we will access POST: / login with a User present in our database.

Spring Boot 3

We have a 200 Status, so authentication has worked.
So we will again access GET: / user /

Spring Boot 4

THE FINAL WORD

This article showed us how to use Spring Security to secure its Spring Boot application. You can find the complete code on GitHub.