Spring Security — H2 Database — Without WebSecurityConfigurerAdapter
In spring security 5.7 version, “WebSecurityConfigurerAdapter” deprecated.
I found a video about new version of spring security, but it was using “InMemoryUserDetailsManager”.
Then I tried to implement my own user details service but got stuck somewhere. After spent hours I found the solution. In this article I will show you how can we implement own UserDetailsService.
Let’s get started!
- Firstly, create a new spring boot project
- Open the project and create WebSecurityConfig class under the config package
We will use SecurityFilterChain instead of overrided configure method in WebSecurityConfigurerAdapter.
to 👇
Okay let’s make a quick test
- Write “HomeController” class
@RestController
@RequestMapping("/api")
@Slf4j
public class HomeController {
@GetMapping("/public")
public String publicEndpoint() {
return "public";
}
@GetMapping("/auth-required")
public String testEndpoint() {
return "auth-required";
}
@GetMapping("/user")
public String user() {
return "user";
}
@GetMapping("/admin")
public String admin() {
return "admin";
}
@GetMapping("/me")
public String admin(Authentication authentication) {
return String.format("User: %s, Role: %s", authentication.getName(), authentication.getAuthorities().toString());
}
}
- Create “BeanConfig” class and register BcryptPasswordEncoder to bean.
@Configuration
public class BeanConfig {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
- Add InMemoryUserDetailManager and DaoAuthenticationProvider to WebSecurityConfig class
InMemoryUserDetailManager: crete Users with username password and authorities and then return all of them.
DaoAuthenticationProvider: If you want to use custom user details service, you can use DaoAuthenticationProvider
. Dont forget to set your custom user details service to auth provider. For now we will set provider toInMemoryUserDetailsManager
. It implements UserDetailsService
.
Postman Time!
User(“gurkan”, ”pass”, ADMIN) and User(“mehmet”, ”pass”, USER)
- GET /api/me
# only authenticated users can access
- GET /api/admin
# only ADMIN role can access
# 403 error for non ADMIN roles
Now Let’s Implement H2 Database!
- update application.properties file
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:database
- create “User” model and “Role” enum
@Entity
@Table(name = "`user`")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String username;
private String password;
@Enumerated(EnumType.STRING)
private Role role;
}public enum Role {
ADMIN, USER
}
- Create “UserRepository” interface
@Repository
public interface UserRepository extends JpaRepository<User,Long> {
Optional<User> findByUsername(String username);
}
- Create “UserService” class
@Service
@Slf4j
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User create(User user) {
return userRepository.save(user);
}
public User findUserByUsername(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("user not found!"));
}
}
- Implement your custom UserDetailsService
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
public UserDetailsServiceImpl(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findUserByUsername(username);
var roles = Stream.of(user.getRole())
.map(x -> new SimpleGrantedAuthority(x.name()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), roles);
}
}
- Write a StartupConfig class for generate test users under the config package
@Component
public class StartupConfig implements CommandLineRunner {
private final UserService userService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public StartupConfig(UserService userService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userService = userService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
public void run(String... args) throws Exception {
userService.create(new User(null, "gurkan", bCryptPasswordEncoder.encode("pass"), Role.ADMIN));
userService.create(new User(null, "mehmet", bCryptPasswordEncoder.encode("pass"), Role.USER));
}
}
Implement changes to “WebSecurityConfig” class
- [Optional] You can add cors config inside “WebSecurityConfig” class
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*");
}
};
}
I have already tested it. If you want to test yourself you can find the postman collection here:
Full Project GitHub Link:
Thanks…
res:
https://spring.io/projects/spring-security
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter