Issue
I need to get the User object details for the currently signed in User so that I can get the customers assigned to the User but I keep getting a null object when I printed to the console. I can sign in successfully with a generated token for the user type , but i keep getting null response when the logged in username is queried by Spring authentication.
Controller
@RestController
public class CustomerController {
@Autowired
CustomerAccountService customerRepo;
@Autowired
UserAccountService userRepo;
@GetMapping(value="marketers/customers")
public List<Customer> getLlistByMarketerName()
{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User loggedInUser = userRepo.findByUserName(authentication.getName());
System.out.println("logged in user:"+ loggedInUser);
return customerRepo.findByMarketer(loggedInUser);
}
The repository
public interface CustomerAccountRepo extends JpaRepository <Customer, Long >
{
@Query("select customer from Customer customer join customer.marketer marketer where marketer = :marketer")
List<Customer> findByMarketer(User marketer);
}
UserDetails Service
@Service
public class UserAccountService implements UserDetailsService {
@Autowired
private UserAccountRepository userRepository;
private PasswordEncoder bCryptPasswordEncoder;
public UserAccountService (PasswordEncoder bCryptPasswordEncoder) {
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserName(username);
if(user == null) {
throw new UsernameNotFoundException("User not found");
}
// List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority(user.getUserRole()));
return MyUserDetails.build(user);
}
JWT Request filter
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
private final UserAccountService userAccountService;
@Autowired
public JwtRequestFilter( @Lazy final UserAccountService userAccountService) {
this.userAccountService = userAccountService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userAccountService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
String authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.joining());
System.out.println("Authorities granted : " + authorities);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
else {
System.out.println("Not Valid Token");
}
}
chain.doFilter(request, response);
}
The User entity
@Entity
public class User {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String firstName ;
private String lastName;
@Column(name="user_name", unique=true)
private String userName;
private String password;
private String Gender;
private String phoneNumber;
private String email;
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@ManyToOne(targetEntity = Branch.class,
fetch = FetchType.LAZY )
@JoinColumn(name="branch_id")
private Branch branch;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date createdDate;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<UserRole> userRole = new HashSet<>();
@Enumerated(EnumType.STRING)
private UserStatus status;
@JsonBackReference
@OneToMany(mappedBy="marketer",cascade = CascadeType.ALL, targetEntity=Customer.class)
private List <Customer> customer;
This is MyUserDetails class that provides the username and password authentication details
public class MyUserDetails implements UserDetails {
private static final long serialVersionUID = -2456373662763511974L;
private Long id;
private String username;
private String password;
private String email;
private Collection<? extends GrantedAuthority> authorities;
public MyUserDetails() {}
public MyUserDetails(Long id, String username, String email, String password,
Collection<? extends GrantedAuthority> authorities)
{
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.authorities = authorities;
}
public static MyUserDetails build(User user) {
List<GrantedAuthority> authorities = user.getUserRole()
.stream().map(role -> new SimpleGrantedAuthority
(role.getName()))
.collect(Collectors.toList());
return new MyUserDetails(user.getId(),
user.getUserName(),
user.getEmail(),
user.getPassword(),
authorities);
}
UPDATED: Security config file . This is my first big project in Java , forgive my errors
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService myUserDetailsService;
public void addResourceHandlers(ResourceHandlerRegistry registry) {
exposeDirectory("customer-photos", registry);
}
private void exposeDirectory(String dirName, ResourceHandlerRegistry registry) {
Path uploadDir = Paths.get(dirName);
String uploadPath = uploadDir.toFile().getAbsolutePath();
if (dirName.startsWith("../")) dirName = dirName.replace("../", "");
registry.addResourceHandler("/" + dirName + "/**").addResourceLocations("file:/"+ uploadPath + "/");
}
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/auth/login", "/validate", "/**").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.formLogin().permitAll()
.and()
.sessionManagement()
.maximumSessions(1)
.and()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/auth/login")
.deleteCookies("JSESSIONID");
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Solution
It seems to me that you don't want all your endpoints to not require authentication. In order to achieve it you will need to remove /**
from your permitAll()
in the security configuration as follows:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/auth/login", "/validate").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.formLogin().permitAll()
.and()
.sessionManagement()
.maximumSessions(1)
.and()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/auth/login")
.deleteCookies("JSESSIONID");
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
Answered By - João Dias