I’m trying to implement JWT auth in spring boot application, but facing some errors. Please help.
This is my security config class
@Configuration
public class SecurityConfig {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private JwtFilter jwtFilter;
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager theUserDetailsManager = new JdbcUserDetailsManager(dataSource);
theUserDetailsManager
.setUsersByUsernameQuery("select user_email, user_password, enabled from chat_user where user_email=?");
theUserDetailsManager.setAuthoritiesByUsernameQuery("select user_email, authority from authorities where user_email=?");
return theUserDetailsManager;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(configurer -> configurer
.requestMatchers("/css/**", "/images/**").permitAll() //initially non authenticated users does not have access to css, so this is added
.requestMatchers("/showSignUpForm","/showLoginForm","/processSignUpForm").permitAll() // allow unauthenticated access to sign up page
.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin(form -> form.loginPage("/showLoginForm")
.loginProcessingUrl("/authenticateTheUser")
.usernameParameter("user_email")
.passwordParameter("user_password")
.defaultSuccessUrl("/showChatPage", true)
.permitAll());
return http.build();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration auth) throws Exception {
return auth.getAuthenticationManager();
}
}
This is my controller
@RestController
@RequestMapping("/public")
@Slf4j
public class PublicController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private ChatUserService userService;
@Autowired
private JwtUtil jwtUtil;
@GetMapping("/health-check")
public String healthCheck() {
return "Ok";
}
@PostMapping("/signup")
public void signup(@RequestBody ChatUser user) {
userService.saveChatUser(user);
}
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody ChatUser user) {
try{
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUser_email(), user.getUser_password()));
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUser_email());
String jwt = jwtUtil.generateToken(userDetails.getUsername());
return new ResponseEntity<>(jwt, HttpStatus.OK);
}catch (Exception e){
log.error("Exception occurred while createAuthenticationToken ", e);
return new ResponseEntity<>("Incorrect username or password", HttpStatus.BAD_REQUEST);
}
}
}
This is my JwtFilter class
@Component
public class JwtFilter extends OncePerRequestFilter{
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt)) {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}
This is my UserDetailsServiceImpl class
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private ChatUserService chatUserService;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
ChatUser user = chatUserService.findByUserEmail(email);
if (user != null) {
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUser_email())
.password(user.getUser_password())
.roles("ROLE_USER")
.build();
}
throw new UsernameNotFoundException("User not found with username: " + email);
}
}
ChatUserService interface
public interface ChatUserService {
public List<ChatUser> getChatUsers();
public void saveChatUser(ChatUser theChatUser);
public ChatUser getChatUser(int theId);
public void deleteChatUser(int theId);
public ChatUser findByUsername(String username);
public ChatUser findByUserEmail(String email);
}
ChatUserServiceImpl
@Service
public class ChatUserServiceImpl implements ChatUserService{
@Autowired
private ChatUserDAO chatUserDao;
@Autowired
private AuthorityDAO authorityDao;
@Override
@Transactional
public List<ChatUser> getChatUsers() {
return chatUserDao.getchatUsers();
}
@Override
@Transactional
public void saveChatUser(ChatUser theChatUser) {
chatUserDao.saveChatUser(theChatUser);
// Create and save the Authority
Authority authority = new Authority(theChatUser.getUser_email(), "ROLE_USER");
authorityDao.saveOrUpdateAuthority(authority);
// Add the Authority to the ChatUser
theChatUser.getAuthorities().add(authority);
}
@Override
@Transactional
public ChatUser getChatUser(int theId) {
return chatUserDao.getChatUser(theId);
}
@Override
@Transactional
public void deleteChatUser(int theId) {
chatUserDao.deleteChatUser(theId);
}
@Override
@Transactional
public ChatUser findByUsername(String username) {
return chatUserDao.findByUsername(username);
}
@Override
@Transactional
public ChatUser findByUserEmail(String email) {
return chatUserDao.findByUserEmail(email);
}
}
Similar interface for ChatUserDAO
My ChatUserDAOImpl class looks like this which is using hibernate session factory
@Repository
public class ChatUserDAOImpl implements ChatUserDAO{
@Autowired
private SessionFactory sessionFactory;
@Override
public List<ChatUser> getchatUsers() {
Session currentSession = sessionFactory.getCurrentSession();
Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser", ChatUser.class);
List<ChatUser> chatUsers = theQuery.getResultList();
return chatUsers;
}
@SuppressWarnings("deprecation")
@Override
public void saveChatUser(ChatUser theChatUser) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(theChatUser);
}
@Override
public ChatUser getChatUser(int theId) {
Session currentSession = sessionFactory.getCurrentSession();
ChatUser theChatUser = currentSession.get(ChatUser.class, theId);
return theChatUser;
}
@Override
public void deleteChatUser(int theId) {
Session currentSession = sessionFactory.getCurrentSession();
Query theQuery = currentSession.createQuery("delete from ChatUser where chat_id=:chatUserId");
theQuery.setParameter("chatUserId", theId);
theQuery.executeUpdate();
}
@Override
public ChatUser findByUsername(String username) {
Session currentSession = sessionFactory.getCurrentSession();
Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser where user_name=:u", ChatUser.class);
theQuery.setParameter("u", username);
try {
return theQuery.getSingleResult();
} catch (NoResultException nre) {
return null;
}
}
@Override
public ChatUser findByUserEmail(String email) {
Session currentSession = sessionFactory.getCurrentSession();
Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser where user_email=:e", ChatUser.class);
theQuery.setParameter("e", email);
try {
return theQuery.getSingleResult();
} catch (NoResultException nre) {
return null;
}
}
}
When i’m trying to run the app, i’m getting this error
Description:
Field userDetailsService in com.springprojects.realtimechatapp.filter.JwtFilter required a single bean, but 2 were found:
– userDetailsServiceImpl: defined in file [C:…serviceUserDetailsServiceImpl.class]
– userDetailsManager: defined by method ‘userDetailsManager’ in class path resource [com/springprojects/…/SecurityConfig.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
@Primary is not allowed on UserDetailsServiceImpl instance in security class. I tried @Primary on UserDetailsManager but now it gives me this error:
Description:
Field sessionFactory in com.springprojects.realtimechatapp.dao.ChatUserDAOImpl required a bean of type ‘org.hibernate.SessionFactory’ that could not be found.
The injection point has the following annotations:
– @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type ‘org.hibernate.SessionFactory’ in your configuration.
I’m confused about how to use one of them.