Spring Security — H2 Database — Without WebSecurityConfigurerAdapter

Gürkan UÇAR
folksdev
Published in
4 min readSep 8, 2022

In spring security 5.7 version, “WebSecurityConfigurerAdapter” deprecated.

I found a video about new version of spring security, but it was using “InMemoryUserDetailsManager”.

Thanks to Dan Vega

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("*");
}
};
}

--

--