I have been trying to get user info from the current session to do some findByUsername like operations. I've tried @AuthenticationPrinciple but even though I fed it with my UserDetails implementation it just returns null. I've also tried SecurityContextHolder method which returns anonymousUser(?). In either way not getting the desired result. Tried all of the solutions I could find on internet so far but no luck. Controller;
@Controller
public class Home {
EntryService entryService;
public Home(EntryService entryService) {
this.entryService = entryService;
}
@GetMapping("/Home")
public String registration(Entry entry, Model model) {
//See what it returns
System.out.println(getUsername());
List<Entry> entries = new ArrayList<>(entryService.getAllEntries());
model.addAttribute("entryList", entries);
model.addAttribute("entry", entry);
return "/home";
}
public String getUsername() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
if (authentication == null)
return null;
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else {
return principal.toString();
}
}
}
Security;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public DetailsService detailsService() {
return new DetailsService();
}
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests().
antMatchers("/register").
permitAll().
antMatchers("/home").
hasRole("USER").
and().
csrf().
disable().
formLogin().
loginPage("/").
permitAll().
passwordParameter("password").
usernameParameter("username").
defaultSuccessUrl("/home").
failureUrl("/error").
and().
logout().
logoutUrl("/logout");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService()).passwordEncoder(passwordEncoder());
}
}
UserDetails;
public class UserDetail implements UserDetails {
private final String username;
private final String password;
private final boolean active;
private final List<GrantedAuthority> roles;
public UserDetail(User user) {
this.username = user.getUserName();
this.password = user.getPassword();
this.active = user.getActive();
this.roles = Arrays.stream(user.getRole().toString().split(",")).
map(SimpleGrantedAuthority::new).
collect(Collectors.toList());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return active;
}
}
And UserDetailsService;
@Service
public class DetailsService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByUserName(s);
user.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return user.map(UserDetail::new).get();
}
}
Using JPA based authentication btw and it works as desired.
The only reason that you would get anonymousUser in the security context is if you are not authenticated. Try adding .anyRequest().authenticated()
right after hasRole("USER").
in your SecurityConfig and then you should see the principal in SecurityContextHolder.getContext().getAuthentication()
. This will continue to work with the methods you've specified as permitAll()
.
Also, just an observation, but your url matcher in your config is on /home
and your controller specifies a GetMapping of /Home
.