Why isn't #Service creating bean? - spring

I have implemented custom UserDetailsService but I can't autowire it because bean is not created. Other services in same package work with same anotations.
#Service
#NoArgsConstructor
public class JwtUserDetailService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new UserPrincipal(userRepository.findByUsername(username));
}
}

Related

Spring security cannot access userService while login NullPointerException

When making post request to login this error shows up telling me that userService is null.
When I create an object of UserService instead of autowiring it it passes it but tell me that the repository called in userService is null. The repository is autowired and i cannot instanciate it because its an interface.
Here is the service class:
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
UserService userService;
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new MyUserDetails(userService.getByUsernameOrEmail(s));
}
}
And this is the security configuration class:
Also I am creating an object of MyUserService because spring cannot autowire it telling me that no bean have such name.
#Configuration
#EnableWebSecurity
public class UserSercurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new MyUserDetailsService());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().hasRole(UserType.ADMIN.toString())
.and().formLogin();
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
You cannot instantiate method or variables in your repository as it is an interface but you can autowire your repository class to use your method declared in userRepository interface, you have to autowire your repository class.You can do it this way as I have done in the below code.
Your service class should be like this:
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = userRepository.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Could not find user");
}
return new MyUserDetails(user);
}
And your repository should be like this:
#Repository
public interface UserRepository extends JpaRepository<Users, Long> {
#Query("SELECT u FROM Users u WHERE u.name = ?1")
public Users getUserByUsername(String username);
}
And also autowire UserDetailsService in your configuration class and pass the instance userDetailsService in your configure(AuthenticationManagerBuilder auth) method, UserDetailsService provides you instance of your MyUserDetailService class.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.and()
.formLogin().permitAll()
.defaultSuccessUrl("/", true)
.and()
.logout().permitAll()
.logoutSuccessUrl("/");
}
Try to implement in this way, If this is solution of your question please let me know and if you still have doubt feel free to ask.

where do I get the "username" value from in spring security to pass to the loadUserByUsername(String username) method of UserDetailsService interface

I am trying to get a user from the database by authenticating the user based on username and password. I am using basic authentication to do this.
I am sending username and password in the authorization header of the rest api
In my controller the getUser() method calls the getuser() method of the UserService class
#GetMapping("/user/self")
public ResponseEntity<UserDto> getUser() {
UserDto UserDto = userService.getUser();
return new ResponseEntity<>(UserDto, HttpStatus.OK);
}
#PutMapping("/user/self")
public ResponseEntity<User> updateUser(#Valid #RequestBody Map<String, String> userMap, Principal principal) {
String username = principal.getName();
String firstname = userMap.get("firstName");
String lastName = userMap.get("lastName");
String password = BCrypt.hashpw(userMap.get("password"), BCrypt.gensalt(10));
User user = userService.getUserByUserName(username);
user.setFirstName(firstname);
user.setLastName(lastName);
user.setPassword(password);
userService.save(user);
return new ResponseEntity<>(user, HttpStatus.NO_CONTENT);
}
UserService class implements UserDetailsService and overrides the loadUserByUsername method that requires a username to be passed as an argument. my question is: how do I pass username to loadUserByUsername() method from my UserService class that I am calling from my controller. where does username value reside?
my understanding is - the Authentication Object contains user credentials that are passed to authentication object when a user types their credentials and send their request, how do I retrieve this username value
#Service
public class UserService implements UserDetailsService {
#Autowired
UserRepository userRepository;
public UserDto save(User user) {
String hashedPassword = BCrypt.hashpw(user.getPassword(), BCrypt.gensalt(10));
user.setPassword(hashedPassword);
userRepository.save(user);
UserDto userDto = new UserDto();
userDto.setId(user.getId());
userDto.setFirstName(user.getFirstName());
userDto.setLastName(user.getLastName());
userDto.setUserName(user.getUserName());
userDto.setAccountUpdatedAt(user.getAccountUpdatedAt());
userDto.setAccountCreatedAt(user.getAccountCreatedAt());
return userDto;
}
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userRepository.findByUserName(userName);
if (user == null) {
throw new UsernameNotFoundException(userName + "was not found");
}
return new UserPrincipal(user);
}
here is my repository code:
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findByUserName(String userName);
}
here is my authentication code:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
UserService userService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
http.authorizeRequests().antMatchers("/v1/user").permitAll()
.antMatchers("/v1/user/self").authenticated().and().httpBasic()
.authenticationEntryPoint(authenticationEntryPoint);
}
}
if you dealing with JPA then in your case you have to use userDetailsService instead of jdbcauthentication, therefor your security class would look like this :
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private UserService userService;
public SecurityConfig(UserService userService){
this.userService = userService;
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10); // Number of rounds
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userService).passwordEncoder(passwordEncoder());
}
}
then you can customize the authentication in the UserService class to satisfy the business need as the below sample :
#Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository){
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByUsername(username);
if(user.isPresent()){
log.info("cretaed under User service : " + user.get());
return user.get();
}
throw new UsernameNotFoundException("empty or invalud user");
}
}
in addition, do not forget to create the findByUsername method in your repository also do not forget to implement org.springframework.security.core.userdetails.UserDetails in your module class:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String name);
}

com.nt.service.JwtUserDetailsService required a bean of type 'com.nt.dao.SpringSecurityDao' that could not be found

#SpringBootApplication
#ComponentScan(basePackages={"com.nt.controller","com.nt.config","com.nt.service","com.nt.dao"})
List item
public class SpringSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityApplication.class, args);
}
//service class
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired(required=true)
private SpringSecurityDao dao;
#Autowired
private PasswordEncoder bcryptEncoder;
public UserDetails loadUserByUsername(String username) {
// TODO Auto-generated method stub
/* UserDao user = dao.findByUsername(username);
if (user == null) {
//dao class
#Repository
public interface SpringSecurityDao extends CrudRepository<UserDao, Integer> {
UserDao findByUsername(String username);
It because you put #Autowired(required=true) , you remove it (required=true)

Session Concurrency not working with custom UserDetails

Spring security maximum concurrent session setting is not working with custom UserDetailsService and custom UserDetails implementation. It allows login with same user from different machines.
But when I use custom UserDetailsService with Spring Security's UserDetails implementation User, it terminates first logged in session and logs in with the new session.
Security Configuration:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic().and()
.sessionManagement().maximumSessions(1).and().and().userDetailsService(customUserDetailsService);
}
}
Custom UserDetailsService with Spring Secutiy User implementation(Working):
#Service
public class CustomUserDetailsService
implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
final User user = new User("user", "password", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
return user;
}
Custom UserDetailsService with Custom User implementation(Not Working):
#Service
public class CustomUserDetailsService
implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
final CustomUser user = new CustomUser();
user.setUsername("user");
user.setPassword("password");
user.setAuthorities(Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
user.setAccountNonLocked(true);
user.setAccountNonExpired(true);
user.setCredentialsNonExpired(true);
user.setEnabled(true);
return user;
}
Any help on this?

Method security not working if I implement UserDetailsService in #Service

I've implemented Spring Security on my project. But method security annotations ignored if I implement UserDetailsService on #Service.
What's wrong with this code?
#Transactional
public interface UserService extends UserDetailsService {
#PreAuthorize("hasRole('ROLE_SUPER')") /* it's ignored. */
void update(UserEditForm form);
#Override
User loadUserByUsername(String username) throws UsernameNotFoundException;
}
#Service
public class SimpleUserService implements UserService {
// ommitted
}
#Transactional
public interface SomeService{
#PreAuthorize("hasRole('ROLE_SUPER')") /* it's working fine */
void doSomething();
}
#Service
public class SimpleSomeService implements SomeService {
// ommitted
}
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
UserService userService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
}
// ommitted
}
ps: Sorry I don't have well enough english knowledge.
You need to make changes in SimpleUserService like this
#Service("simpleUserService")
public class SimpleUserService implements UserService {
// ommitted
}
and in SecurityConfig, Autowire UserService with #Qualifier("simpleUserService")
#Autowired
#Qualifier("simpleUserService")
UserService userService;
Do not annotate the interfaces with Spring annotations(#Component, #Service, #Transactional, #Repository), Add the annotations on the implementation classes.
Remove #Transactional on UserService interface, Create new class UserServiceImpl and add annotation #Transactional.
and also move the #PreAuthorize("hasRole('ROLE_SUPER')") annotation on to the implementation method.

Resources