Reputation: 315
I have a REST API and want to secure it with Spring Security. I followed the tutorial here: https://github.com/Zuehlke/springboot-sec-tutor My whole project can be found here: https://github.com/YanickSchraner/wodss-tippspiel_backend
The problem I am facing is, that @AuthenticationPrincipal is returning Null even after performing a successful login POST request. I assume, that because of that the @PreAuthorized annotation isn't working either.
Here is my Security Config:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RESTAuthenticationSuccessHandler restAuthenticationSuccessHandler;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private AuthenticationManager authenticationManager;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception{
return super.authenticationManager();
}
@Bean
public RESTAuthenticationFilter restAuthenticationFilter() {
RESTAuthenticationFilter restAuthenticationFilter = new RESTAuthenticationFilter(objectMapper);
restAuthenticationFilter.setAuthenticationManager(authenticationManager);
restAuthenticationFilter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler);
return restAuthenticationFilter;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and().anonymous().disable()
.csrf().disable() // CSRF protection is done with custom HTTP header (OWASP suggestion)
.addFilterBefore(new XRequestedWithHeaderFilter(), CsrfFilter.class)
.addFilterBefore(new EnforceCorsFilter(), CsrfFilter.class)
.addFilterBefore(restAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.logout().logoutSuccessHandler((request, response, authentication) -> response.setStatus(HttpServletResponse.SC_OK))
.and()
.headers()
.frameOptions().sameOrigin()
.contentSecurityPolicy("default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri /csp")
.and()
.httpStrictTransportSecurity()
.maxAgeInSeconds(63072000);
http
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.deleteCookies("BettingGame_SchranerOhmeZumbrunn_JSESSIONID");
http
.sessionManagement()
.sessionFixation()
.newSession();
}
}
Here is my UserDetailsServiceImpl:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Value("${security.login.errormessage}")
private String errorMessage;
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUserByNameEquals(username)
.orElseThrow(() -> new UsernameNotFoundException(errorMessage));
HashSet<GrantedAuthority> authorities = new HashSet<>();
if(user.getRoles() != null){
user.getRoles().stream()
.map(Role::getName)
.map(SimpleGrantedAuthority::new)
.forEach(authorities::add);
}
return new org.springframework.security.core.userdetails.User(user.getName(),user.getPassword(), authorities);
}
}
Here is a part of my Controller where I get Null from @AuthenticationPricipal and @PreAuthorized is returning a 403 even after performing a successful login:
@RestController
@RequestMapping("/users")
//@PreAuthorize("hasRole('USER')")
public class UserController {
private final UserService service;
@RequestMapping(value = "/self",method = RequestMethod.GET)
public ResponseEntity<User> getLogedInUser(@AuthenticationPrincipal User user){
return new ResponseEntity<>(user, HttpStatus.OK);
}
@Autowired
public UserController(UserService service) {
this.service = service;
}
@GetMapping(produces = "application/json")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<List<User>> getAllUsers() {
return new ResponseEntity<>(service.getAllUsers(), HttpStatus.OK);
}
Upvotes: 1
Views: 4457
Reputation: 315
I finally figured it out. Two simple mistakes: 1. @AuthenticationPrinciple was Null because I was asking for an User object but my UserDetailsServiceImpl was storing / returning a UserDetails object. By making my User domain object implementing the UserDetails interface and making my UserDetailServiceImpl returning that User object this isseue was fixed.
The working code can be found an this public github project: https://github.com/YanickSchraner/wodss-tippspiel_backend
Upvotes: 3