Spring Boot Order of Initialization for reading from Database - java

I am currently using hibernate repositories and I need roles in my database, as one of my service layers uses the roles at startup.
#SpringBootApplication
public class FedditBackendApplication {
public static void main(String[] args) {
SpringApplication.run(FedditBackendApplication.class, args);
}
#Bean
CommandLineRunner init (RoleRepository roleRepo) {
return args -> {
for(ERole role: ERole.values()) {
if (!roleRepo.existsByName(role)){
roleRepo.save(new Role(role));
}
}
};
}
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
To initialize my roles into my database, I let spring do my schema creation, however I need to put these rows into the database directly afterwards.
Is There a better way to do this? The issue I'm running into is that one of my Service components gets from the roleRepository in the constructor and it's not finding anything when I use this initialization method.
#Service
#Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final AuthenticationManager authenticationManager;
private final RoleRepository roleRepository;
private final PasswordEncoder encoder;
private final JwtUtils jwtUtils;
private final Set<Role> userRoles;
public UserServiceImpl(UserRepository userRepository, AuthenticationManager authenticationManager, RoleRepository roleRepository, PasswordEncoder encoder, JwtUtils jwtUtils,Set<Role> userRoles){
this.userRepository = userRepository;
this.authenticationManager = authenticationManager;
this.roleRepository = roleRepository;
this.encoder = encoder;
this.jwtUtils = jwtUtils;
this.userRoles = new HashSet<>();
Optional<Role> userRole = this.roleRepository.findByName(ERole.ROLE_USER);
// throws error on next line because userRole is not present in the DB
this.userRoles.add(userRole.get());
}
}
I've tried changing the order of the bean initialization with no success.
It seems that the CommandLineRunner bean initializes after the service layers.
#Bean
CommandLineRunner init (RoleRepository roleRepo) {
return args -> {
for(ERole role: ERole.values()) {
if (!roleRepo.existsByName(role)){
roleRepo.save(new Role(role));
}
}
};
}
I'd like to initialize these rows after the schema is created, though I'm not really sure how.
https://github.com/vw0389/feddit-backend/tree/comments is the repo and branch if relevant

Related

#Autowired service bean in Controller Class won't recognize the methods

I have this classes:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private EntityManager entityManager;
#Override
public UserDetails loadUserByUsername( String username) throws UsernameNotFoundException {
Optional<User> userOptional = userRepository.findUserByUsername(username);
User user = userOptional.orElseThrow(
()->new UsernameNotFoundException("Username not found in the database")
);
return new MyUserDetails(user);
}
public void saveUser(User user){
User newUser=new User();
newUser.setUsername(user.getUsername());
newUser.setPassword(passwordEncoder.encode(user.getPassword()));
newUser.setEnabled(true);
newUser.setRoles(List.of(entityManager.find(Role.class,1)
));
userRepository.save(newUser);
}
public void deleteUser(User user){
userRepository.delete(user);
}
public UserDetails getCurrentlyLoggedUser(Authentication authentication){
Object principal = authentication.getPrincipal();
if(principal instanceof UserDetails){
String username=((User) principal).getUsername();
UserDetails loggedUser = loadUserByUsername(username);
return loggedUser;
}
return null;
}}
#Controller
#RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
#Autowired
private final UserDetailsService userDetailsService;
#GetMapping("/orderlist")
public String showOrdeList(Model model, #AuthenticationPrincipal Authentication authentication){
userDetailsService.getCurrentlyLoggedUser
}
}
I want to know why the methods from UserDetailsServiceImpl class won't show up in the controller...
I can acces only the methods from the interface, but not the ones implemented besides them.
I tried to use qualifiers, and even made a config class to instantiate the impl class when injecting the bean by interface.
#Configuration
public class OrderConfig {
#Bean("userDetailsService")
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
Can anyone tell me what I'm doing wrong?
Methods defined in UserDetailsServiceImpl but not in UserDetailsService aren't accessible because you're injecting your service by interface UserDetailsService and compiler doesn't know which implementation will be injected in runtime.
So, you need to define your own interface with all methods that you want to expose or inject your service by class.
#Autowired
private final UserDetailsServiceImpl userDetailsService;
I would recommend a custom interface that extends UserDetailsService and injecting your service using your custom interface.

In Springboot Unit test, MockMvc returns 403 Forbidden

In Springboot unit Test always return 403 error,I've tried a variety of different configurations using AutoConfigureMockMvc with secure false and excluding security auto configuration getting 403 errror. Can anyone help me on this.
Here is my Security Implementation
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationEventPublisher(authenticationEventPublisher())
.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.anonymous()
.disable()
.authorizeRequests()
.antMatchers("/api-docs/**")
.permitAll();
}
#Bean
public DefaultAuthenticationEventPublisher authenticationEventPublisher() {
return new DefaultAuthenticationEventPublisher();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
Sharing Api Implementation Class, added PreAuthorize -Admin, to view all users
#RestController
#RequestMapping("/api/userInfo")
public class UserController {
private final Logger LOG = Logger.getLogger(getClass());
private String serviceMsg = "serviceMsg";
#Autowired
private UserService userService;
#Autowired
private UserServiceUtil util;
#PreAuthorize("hasAnyRole('ADMIN')")
#RequestMapping(method = RequestMethod.GET, produces = "application/json" )
#ApiOperation(value = "Get details of all RA2 users in a paginated JSON format")
public Page<User> listUser(Pageable pageable) {
return userService.getUserSummary(pageable);
}
And Here is my JUnit Test ,am Sending get request and return 403 error.
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration
#AutoConfigureMockMvc(addFilters = false)
public class UserControllerTest {
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class TestConfiguration {
#Bean
#Primary
public UserService getUserService(){
return Mockito.mock(UserService.class);
}
#Bean
#Primary
public UserServiceUtil getUserServiceUtil(){
return Mockito.mock(UserServiceUtil.class);
}
}
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext ;
private String serviceMsg = "serviceMsg";
#Autowired
private UserService userService;
#Autowired
private UserServiceUtil util;
private User admin;
private User user;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext ).apply(springSecurity()).build();
}
#WithMockUser(username = "test",authorities ="ADMIN")
#Test
public void getuserList() throws Exception {
List<User> list = new ArrayList<User>();
list.add(new User());
Page<User> page = new PageImpl<User>(list, null, list.size());
Mockito.when(userService.getUserSummary(any(Pageable.class))).thenReturn(page);
this.mockMvc.perform(get("/api/userInfo?page=1&size=10").with(csrf()).contentType(MediaType.APPLICATION_JSON)).
andExpect(status().isOk()).andDo(MockMvcResultHandlers.print());
}
```
There is a difference between authorities and roles when you use #WithMockUser:
/**
* <p>
* The roles to use. The default is "USER". A {#link GrantedAuthority} will be created
* for each value within roles. Each value in roles will automatically be prefixed
* with "ROLE_". For example, the default will result in "ROLE_USER" being used.
* </p>
* <p>
* If {#link #authorities()} is specified this property cannot be changed from the
* default.
* </p>
* #return
*/
String[] roles() default { "USER" };
/**
* <p>
* The authorities to use. A {#link GrantedAuthority} will be created for each value.
* </p>
*
* <p>
* If this property is specified then {#link #roles()} is not used. This differs from
* {#link #roles()} in that it does not prefix the values passed in automatically.
* </p>
* #return
*/
String[] authorities() default {};
Whatever you set with authorities does not get any prefix.
As your controller expects ROLE_ADMIN, try using roles instead.
In addition to this, I would also try to use a sliced Spring Context using #WebMvcTest for this test. Starting the whole Spring Context using #SpringBootTest is not required for such tests.
Removed #SpringBootTest and added #WebMvcTest and roles, but getting 403.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = UserController.class)
#ActiveProfiles("test")
#ContextConfiguration
#AutoConfigureMockMvc(addFilters = false)
public class UserControllerTest {
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class TestConfiguration {
#Bean
#Primary
public UserService getUserService(){
return Mockito.mock(UserService.class);
}
#Bean
#Primary
public UserServiceUtil getUserServiceUtil(){
return Mockito.mock(UserServiceUtil.class);
}
}
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext wac;
#Autowired
private UserService userService;
#Autowired
private UserServiceUtil util;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).apply(springSecurity()).build();
}
#WithMockUser(username = "Ram",roles ="ADMIN")
#Test
public void getuserList() throws Exception {
List<User> list = new ArrayList<User>();
Page<User> page = new PageImpl<User>(list, null, list.size());
Mockito.when(userService.getUserSummary(any(Pageable.class))).thenReturn(page);
this.mockMvc.perform(get("/api/userInfo?page=1&size=10").with(csrf()).contentType(MediaType.APPLICATION_JSON)).
andExpect(status().isOk()).andDo(MockMvcResultHandlers.print());
}
}

Spring Security, Problem with PasswordEncoder

i have a problem with PasswordEncoder,
my code:
#Service
public class UserService {
private static final String DEFAULT_ROLE = "ROLE_USER";
private UserRepository userRepository;
private UserRoleRepository roleRepository;
public PasswordEncoder passwordEncoder;
#Autowired
public UserService(PasswordEncoder passwordEncoder){
this.passwordEncoder = passwordEncoder;
}
#Autowired
public void setUserRepository(UserRepository userRepository){
this.userRepository = userRepository;
}
#Autowired
public void setUserRoleRepository(UserRoleRepository roleRepository){
this.roleRepository = roleRepository;
}
public void addWithDefaultRole(User user){
UserRole defaultRole = roleRepository.findByRole(DEFAULT_ROLE);
user.getRoles().add(defaultRole);
String passwordHash = passwordEncoder.encode(user.getPassword());
user.setPassword(passwordHash);
userRepository.save(user);
}
}
error:
APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in org.spring.service.UserService required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
Process finished with exit code 1
I don't know how to fix it.
Try this way ( One of two bean, not both):
#Configuration
#EnableWebSecurity
public class WebSecurityConfigAuthentication extends WebSecurityConfigurerAdapter {
// For BCrypt Encoded password
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
OR
// For no Encoder, plain text password
#Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
For dependency injection to work, you need to acually declare a bean of the appropriate type, otherwise there is nothing to be injected. Exactly that is telling you the exception.
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // or any other password encoder
}

Error init SpringBoot v.2.1.10.RELEASE app Parameter 0 of constructor

I have a SpringBoot app with this config file:
package com.bonanza.web.config;
#Configuration
#EnableJpaRepositories(basePackages = "com.bonanza.backend.repository")
#EntityScan(basePackages = "com.bonanza.backend")
#EnableTransactionManagement
#EnableCaching
#PropertySource("file:///${user.home}/.bonanza/application-common.properties")
public class BonanzaApplicationConfig {
}
and this service:
package com.bonanza.backend.service;
#Service
#Transactional(
readOnly = true
)
public class UserService {
private final RoleRepository roleRepository;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final PasswordResetTokenRepository passwordResetTokenRepository;
public UserService(RoleRepository roleRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, PasswordResetTokenRepository passwordResetTokenRepository) {
this.roleRepository = roleRepository;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.passwordResetTokenRepository = passwordResetTokenRepository;
}
..
}
and the main class:
package com.bonanza.web
#SpringBootApplication
public class BonanzaWebApplication {
public static void main(String[] args) {
SpringApplication.run(BonanzaWebApplication.class, args);
}
}
and this controller
package com.bonanza.web.controller;
#Controller
public class AppErrorController implements ErrorController {
protected final UserService userService;
..
public AppErrorController(UserService userService, ErrorAttributes errorAttributes, EmailService emailService) {
super(userService);
this.errorAttributes = errorAttributes;
this.emailService = emailService;
}
...
}
but when I start the app. I have this error:
Description:
Parameter 0 of constructor in com.bonanza.web.controller.AppErrorController required a bean of type 'com.bonanza.backend.service.UserService' that could not be found.
Action:
Consider defining a bean of type 'com.bonanza.backend.service.UserService' in your configuration.
#SpringBootApplication, Will only search the current package and all its sub packages for components/beans. Your UserService package
com.bonanza.backend.service
is not a subpackage of BonanzaWebApplication
com.bonanza.web
So you can use with all the packages that need to be Component Scan
#ComponentScan({"com.bonanza.web","com.bonanza.backend.service"})
#SpringBootApplication
public class BonanzaWebApplication {
public static void main(String[] args) {
SpringApplication.run(BonanzaWebApplication.class, args);
}
}
You can also specify component scanning in #SpringBootApplication annotation it self
#SpringBootApplication(scanBasePackages = {"com.bonanza.web","com.bonanza.backend.service"})

#Transactional not rolling back with RuntimeException

I am creating simple spring boot application. I used spring transaction management to handle the transaction. Here is my code.
ServiceImpl class
#Service("orderService")
public class OrderServiceImpl implements OrderService {
#Autowired
private CustomerDao customerDao;
#Autowired
private OrderDao orderDao;
#Transactional(rollbackFor = Exception.class)
#Override
public Long placeOrder(OrderPlacementRequest request) {
customerDao.save(request.getCustomer());
return orderDao.placeOrder(request.getOrder());
}
}
OrderDaoImpl class,
#Repository("orderDao")
public class OrderDaoImpl extends AbstractHibernateDao implements OrderDao {
#Override
public Long placeOrder(Order order) {
throw new RuntimeException("Test Error Message");
}
}
Configuration class,
#Configuration
#EnableTransactionManagement
public class HibernateConfig {
//Other Configurations
#Autowired
private final Environment environment;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(environment.getRequiredProperty("spring.datasource.url"));
dataSource.setUsername(environment.getRequiredProperty("spring.datasource.username"));
dataSource.setPassword(environment.getRequiredProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
}
application.properties
spring.aop.proxy-target-class=true
OrderController class
#RestController
#RequestMapping("/order")
public class OrderController {
#Autowired
private OrderService orderService;
#RequestMapping(value = "/place", method = RequestMethod.POST)
public Long placeOrder(#RequestBody OrderPlacementRequest request) {
return orderService.placeOrder(request);
}
}
In my case even though second method placeOrder failed. Customer will be saved in mysql database. What I wanted is to rollback the customer saving method. I went through few articles on transaction management including spring docs and stackoverflow. Still can not find the problem.
Updated with Controller class.

Categories