Changing Spring XML configuration to #Configuration - java

My spring application is already configured via xml stye.I am trying to change it with #Configuration classes.
My app is used another project as maven library.I have a service, annotated with #Named and this service is used by another service in the library.
#Named("userDetailsService")
public class UserDetailsServiceImpl extends AbstractServiceImpl implements UserDetailsService {
#Inject
private UserService userService;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.getByUserName(username);
}
}
#Named("userService")
public class UserServiceImpl extends BaseDaoServiceImpl<User, UserDao> implements UserService {
#Inject
private AuthorityService authorityService;
#Inject
private UserAuthorityService userAuthorityService;
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User getByUserName(String username) {
return dao.getByUserName(username);
}
#Override
public List<User> getUserWithHasAuthority(String authorityName) {
return dao.getUserWithHasAuthority(authorityName);
}
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User insert(User user) {
user.setEnabled(true);
super.insert(user);
Authority authority = authorityService.getByName("ROLE_USER");
UserAuthority userAuthority = new UserAuthority();
userAuthority.setAuthority(authority);
userAuthority.setUser(user);
userAuthorityService.insert(userAuthority);
return user;
}
}
On my new #Configuration class
#Configuration
#ComponentScan(basePackages = {"com.mylibrary","com.myapp"})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableTransactionManagement
public class ServiceTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and this is my test method:
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
it gives null pointer exception on userService property that exist in userDetailsService.Both of them #Named.
Thnx for your help
Test classes :
#ContextConfiguration(classes = {ServiceTestConfiguration.class, DataSourceConfiguration.class, SecurityConfiguration.class})
#Transactional
public class AbstractTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
public class ServiceTest extends AbstractTest {
#Inject
private AuthenticationManager authenticationManager;
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
}

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.

Field myUserDetailsService in com.api.WebSecurityConfig required a single bean, but 2 were found

Full error:
Field myUserDetailsService in com.api.WebSecurityConfig required a single bean, but 2 were found:
user1: defined in file [/tools/tomcat/instances/webapps/api/WEB-INF/classes/com/api/jwt/users/test1.class]
user2: defined in file [/tools/tomcat/instances/webapps/api/WEB-INF/classes/com/api/jwt/users/test2.class]
I have the following classes:
Test1.java
#Service
#Component("user1")
public class Test1 implements UserDetailsService {
#Value("${test1.username}")
private String test1Username;
#Value("${test1.password}")
private String test1Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test1Username)) {
return new User(username, test1Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
Test2.java
#Service
#Component("user2")
public class Test2 implements UserDetailsService {
#Value("${test2.username}")
private String test2Username;
#Value("${test2.password}")
private String test2Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test2Username)) {
return new User(username, test2Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
SpringMainApplication.java
#EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/**/access-token").permitAll().
anyRequest().authenticated().and().
exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
#Qualifier(value = "user1")
private Test1 test1;
#Autowired
#Qualifier(value = "user2")
private Test2 test2;
#Override
protected void doFilterInternal() throws ServletException, IOException {
// SOME CODE
}
I thought that by using #Component and #Qualifier, I would be able to set up two user detail classes, but it doesn't seem to be the case.
Is there anything that I am missing or is it just not possible in this design?
If I make another class named MyUserDetailsService.java then compiling works and the correct classes are being accessed in the respective endpoints. I just don't understand why MyUserDetailsService must exist and I cannot use the Test1 and Test2 as user classes.
EDIT:
Test1.java
#Service
#Component("myUserDetailsService")
public class Test1 implements UserDetailsService {
#Value("${test1.username}")
private String test1Username;
#Value("${test1.password}")
private String test1Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test1Username)) {
return new User(username, test1Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
Test2.java
#Service
#Component("myUserDetailsService")
public class Test2 implements UserDetailsService {
#Value("${test2.username}")
private String test2Username;
#Value("${test2.password}")
private String test2Password;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username != null && username.equals(test2Username)) {
return new User(username, test2Password, new ArrayList<>());
} else {
throw new UsernameNotFoundException("Username not found: " + username);
}
}
}
SpringMainApplication.java
#EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier(value = "myUserDetailsService")
private UserDetailsService myUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/**/access-token").permitAll().
anyRequest().authenticated().and().
exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private Test1 test1;
#Autowired
private Test2 test2;
#Override
protected void doFilterInternal() throws ServletException, IOException {
// SOME CODE
}
This gives me the following error:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.api.SpringMainApplication];
nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'myUserDetailsService' for bean class [com.api.jwt.users.Test2DetailsService] conflicts with existing, non-compatible bean definition of same name and class [com.api.jwt.users.Test1DetailsService]
You don't need qualifiers for these declarations. Spring can inject those by type as there's a single matching bean for each type (Test1 and Test2).
#Autowired
#Qualifier(value = "user1")
private Test1 test1;
#Autowired
#Qualifier(value = "user2")
private Test2 test2;
However you do need need a qualifier for this declaration because you have two implementations of UserDetailsService and spring doesn't know which one to inject, resulting in your error.
#Autowired
private UserDetailsService myUserDetailsService;
Edit
In your updated example you are defining two beans with the same bean id
#Component("myUserDetailsService")
public class Test1 implements UserDetailsService {
#Component("myUserDetailsService")
public class Test2 implements UserDetailsService {
This causes the new error you're seeing (It might help to review Spring documentation):
Annotation-specified bean name 'myUserDetailsService' for bean class
[com.api.jwt.users.Test2DetailsService] conflicts with existing,
non-compatible bean definition of same name and class
[com.api.jwt.users.Test1DetailsService]
In your example it doesn't really make sense to have two UserDetailsService implementations. You could refactor by combining them into a single class.

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"})

User account is locked while signing in

I'm starting to learn Spring Security now and I got with trouble. I wrote configuration classes, getting data from DB and so on, but in my webpage I see the message "User account is locked" and error parameter in url after signing in.
MessengerApplication.java
#SpringBootApplication
public class MessengerApplication {
public static void main(String[] args) {
SpringApplication.run(MessengerApplication.class, args);
}
}
MainPageController.java
#RestController
public class MainPageController {
#RequestMapping("/")
public ModelAndView greeting() {
Map<String, Object> model = new HashMap<>();
model.put("data", "world");
return new ModelAndView("main_page", model);
}
}
SecurityConfig.java
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userService;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
UserServiceImpl.java
#Service
public class UserServiceImpl implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public MyUserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userRepository.findUserByName(s);
if (user == null)
throw new UsernameNotFoundException(s);
return new MyUserDetails(user);
}
}
UserRepositoryImpl.java
#Repository
public class UserRepositoryImpl implements UserRepository {
#Autowired
JdbcTemplate template;
#Override
public User findUserByName(String name) {
return template.queryForObject("select * from users where name = ?", rowMapper, name);
}
private RowMapper<User> rowMapper = new RowMapper<User>() {
#Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setPassword(resultSet.getString("password"));
user.setName(resultSet.getString("name"));
user.setId(resultSet.getLong("id"));
return user;
}
};
}
UserRepository.java
public interface UserRepository {
User findUserByName(String name);
}
User.java
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false, unique = true)
private String name;
private String password;
// get(), set()
}
MyUserDetails.java
public class MyUserDetails implements UserDetails {
private User user;
public MyUserDetails(User user) {
this.user = user;
}
// ...
}
The method is isAccountNonLocked, emphasis on non. You need to return true from this method in order to have an 'unlocked' account. Same thing with the method that pertains to 'expired', etc. In this case true means allow it, false means reject it.

Mock objects returns null

I have below Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfig.class)
public class UserServiceTest {
#Inject
private UserRepository userRepository;
#Inject
private UserService userService;
#Test
public void testProcessInvoice() throws SQLException {
User user = new User();
user.setFirstName("abc");
when(userRepository.save(any(User.class))).thenReturn(user);
Assert.assertNotNull(userService);
User savedUser = userService.save(user);
Assert.assertEquals("abc", savedUser.getFirstName());
}
}
I have below SpringTestConfig.java
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return Mockito.mock(UserService.class);
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
call to User savedUser = userService.save(user); returns null user object. I am not able to figure it out why it is returning null.
EDIT:
UserRepository is JpaRepository, if this is a problem
public interface UserRepository extends JpaRepository<User, Long> {
}
Your UserService is a mock object, and has no defined behavior for dealing with the #save(User) method.
Mocking the object under test is probably not what you are after here. I would recommend your objects under test are instantiated in the test, and injected with the mocks or stubs of the objects that they utilize.
Your configuration needs to return a real UserService:
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return new UserServiceImpl(); // or whatever your implementation is
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
Mocks are for collaborators, not for the thing you're testing.

Categories