Getting started with Spring Security - Authentication and Authorization
Tags: java, security, spring
Published: 2020-05-10 10:43:28

I've been reading a bit about spring security in the last days so I'm going to share some of the things I have learned.

**Authentication:** confirm users identity.
**Authorization:** what kind of permissions users have.

Begin by adding Spring Boot Starter Security dependency to pom.xml to enable basic authentication.
Create an endpoint in your controller and try to consume it, a login form provided by spring security will be shown.

<img src="https://dev-to-uploads.s3.amazonaws.com/i/qzs5v7p9irhenfhkimkx.png" alt="login form" width="500"/>

Have a look at the console when you run the application, you should see a an auto-generated password, username is by default 'user'.

<img src="https://dev-to-uploads.s3.amazonaws.com/i/ewtw3ly1cbrxrrvc73ko.png" alt="console message" width="500"/>

We can even configure our own user credentials in application.properties
```java
spring.security.user.name=jhoni
spring.security.user.password=1234
```

To create a customised security class, we need to use @EnableWebSecurity and extends the class with @WebSecurityConfigurerAdapter so that we can redefine some of the methods provided by this class. Spring security also enforces you tu hash your passwords so that they are not saved in plain text. For the next example we can use *PasswordEncoder*, of course this should not be used in production, but for this example is fine. An alternative could be <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.html" target="_blank">BCryptPasswordEncoder</a>.

##Authentication
In intelliJ cmd+N and have a look at the methods you can override. We are going to use *configure* method with *AuthenticationManagerBuilder* param. *auth* has different methods like like *jdbcAuthentication*, *ldapAuthentication*, *userDetailsService*... but we will use *inMemoryAuthentication* for this basic example. As its name says, user credentials are stored in memory.

```java
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user1")
                .password("123")
                .roles("APPRENTICE")
                .and()
                .withUser("user2")
                .password("123")
                .roles("SENSEI");
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
```
<span></span><br>
##Authorization
Here we define which URL paths should be secured and which should not. We now use the *configure* method with *HttpSecurity* param. I'm giving access to any user who has role SENSEI.

```java
@Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**").hasRole("SENSEI")
                .and().formLogin();

    }
```

If we try to log in with user2, access will be granted. The /** means allow access to current level and any inner levels. We can create different endpoints and give different restrictions as the following example. It's important to know that the **most restrictive rules should be at the top.**

```java
@Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/sensei").hasRole("SENSEI")
                .antMatchers("/apprentice").hasRole("APPRENTICE")
                .antMatchers("/").permitAll()
                .and().formLogin();
    }
```
If you would like to have different roles try *hasAnyRole()*. We can also add filters, their goal is to intercept requests and each of them have their own responsibility (we will add one later)

```java
.hasAnyRole("ROLE1", "ROLE2", "ROLE3")
.addFilterBefore()
.addFilterAfter()
```
<span></span><br>
## UserDetailsService
We are going to configure Spring Security to depend on a UserDetailsService. It basically loads user-specific data. *auth* has a method called *userDetailsService()* that allows a customised authentication based on the UserDetailsService interface
```java
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService myUserDetailService;

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

Let's create our own UserDetailService and UserDetail class. In *MyUserDetailService* we override *loadUserByUsername* method that will receive the username as a parameter and pass it to our *MyUserDetails*.

```java
@Service
public class MyUserDetailService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new MyUserDetails(username);
    }
}
```

When we implement UserDetails interface, we can override several methods. I'm going to create a field *username* to get the username passed when user logs in. The rest of the methods will have harcoded values. *getAuthorities* returns the permissions granted to the user, in this case I will just add the SENSEI role.
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyUserDetails implements UserDetails {

    private String username;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() { 
        return List.of(new SimpleGrantedAuthority("ROLE_SENSEI"));
    }

    @Override
    public String getPassword() {
        return "pass";
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
```

The flow is as follows:
1. WebSecurity class is loaded
2. When user types username + password, the authentication filter intercepts the request and a UsernamePasswordAuthenticationToken object is created with the request credentials.
3. loadUserByUsername receives username.
4. A MyUserDetails object is created with whatever username is sent and everything else hardcoded(pretending to be a user that we have in our database) and it is compared with the UsernamePasswordAuthenticationToken object.
4. If everything is okay 😃, otherwise ⛔️

Instead of hardcoding the creation of MyUserDetails in the service, you can inject the repository and retrieve the user details from database. Of course, the *MyUserDetails* class would have to be changed so that instead of receiving a string as we did before, it would receive a *User*.

```java
@Service
@RequiredArgsConstructor
public class MyUserDetailService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
              .orElseThrow(() -> new UsernameNotFoundException("Username does not exist"));
        return new MyUserDetails(user);
    }
}
```

I'm going to continue by adding JWT in another post so this one doesn't get too long.