Spring Security -- 1) Basic Concepts
In these short series , I am going to dive into what Spring Security is, how Spring Security works. Most of the example application would be for web …
In this post, I am going to use real database to check the user against the request(s).
If you only need to see the code, here is the github link
I am going to use Postgresql, and I created new spring boot application with these dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
And there is also one endpoint in the project:
package com.mehmetozanguven.springsecuritywithdatabase.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
Do not forget to add database configuration in the application.properties file:
# Spring DATASOURCE for local postgresql
spring.datasource.url=jdbc:postgresql://localhost:5432/testdatabase
spring.datasource.username=postgres
spring.datasource.password=1234
# For localdb, it would be good to re-create all-tables
spring.jpa.hibernate.ddl-auto=update
Please create the table and initialize with predefined data:
In our example, because I use the NoOpPasswordEncoder, I do not need to find hashcode for the dummy inserted user.
Do not use NoOpPasswordEncoder in the production
[mehmetozanguven@localhost ~]$ sudo -iu postgres
[postgres@localhost ~]$ psql
postgres=# \c testdatabase
You are now connected to database "testdatabase" as user "postgres".
testdatabase=# CREATE TABLE IF NOT EXISTS test_users (id serial PRIMARY KEY, username VARCHAR(50), password VARCHAR(50));
testdatabase=# INSERT INTO test_users (username,password) VALUES ('dummy_user','1234');
DTO class:
package com.mehmetozanguven.springsecuritywithdatabase.entity;
@Entity
@Table(name = "test_users")
public class UserDTO {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
// getters and setters
}
Repository to connect the database:
package com.mehmetozanguven.springsecuritywithdatabase.repository;
@Repository
public interface UserRepository extends JpaRepository<UserDTO, Long> {
Optional<UserDTO> findUserByUsername(String username);
}
And Spring Security needs to work with UserDetails
object, therefore I should somehow convert to the UserDTO to the UserDetails
object. I have created a class for that:
package com.mehmetozanguven.springsecuritywithdatabase.services;
import com.mehmetozanguven.springsecuritywithdatabase.entity.UserDTO;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class SecureUser implements UserDetails {
private final UserDTO userDTO;
public SecureUser(UserDTO userDTO) {
this.userDTO = userDTO;
}
/**
* Because I will not look at the authority part for now,
* I am just creating dummy authority for the users
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(() -> "read");
}
@Override
public String getPassword() {
return userDTO.getPassword();
}
@Override
public String getUsername() {
return userDTO.getUsername();
}
/**
* Because we do not have any logic for this method
* just return true to pass.
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* Because we do not have any logic for this method
* just return true to pass.
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* Because we do not have any logic for this method
* just return true to pass.
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* Because we do not have any logic for this method
* just return true to pass.
*/
@Override
public boolean isEnabled() {
return true;
}
}
Before diving into details, that would be good to have picture describes the overall architecture:
Because I am going to find out whether there is a user for that request in our database, let’s create userDetailsService namely PostgresqlUserDetailsService
:
package com.mehmetozanguven.springsecuritywithdatabase.services;
public class PostgresqlUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username){
Optional<UserDTO> userDTOOptional = userRepository.findUserByUsername(username);
UserDTO userInDb = userDTOOptional.orElseThrow(() -> new UsernameNotFoundException("Not Found in DB"));
return new SecureUser(userInDb);
}
}
Here is the configuration (this is not a security configuration that you would expect, in this configuration i am just creating my own beans for userdetailsService
and passwordEncoder
therefore spring will use my beans instead of the default ones):
package com.mehmetozanguven.springsecuritywithdatabase.config;
@Configuration
public class ProjectBeanConfiguration {
@Bean
public UserDetailsService userDetailsService(){
return new PostgresqlUserDetailsService();
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
After running the project, here is the curl command to access the endpoint:
[mehmetozanguven@localhost ~]$ curl --user dummy_user:1234 -X GET http://localhost:8080/hello
hello
I will continue with the next one…
In these short series , I am going to dive into what Spring Security is, how Spring Security works. Most of the example application would be for web …
This is the second post for a short series of aspect oriented programming with the spring boot. In this post, I am going to implement a simple project …