libnull-dev libnull-dev - 2 months ago 13
Java Question

Sending credentials in secured spring-boot application using fetch API

I was following this tutorial with the only difference that I used Fetch API for querying REST repository. My problem is that after successful authorization every

fetch()
call returns response containing login form html string. Specifically, this kind of code

fetch(`${root}/employees?size=${pageSize}`).then( p => p.json()})


produces the following error in Chrome console:

localhost/:1 Uncaught (in promise) SyntaxError: Unexpected token < in
JSON at position 0


If I add a header with authorization to
fetch
:

fetch(`${root}/employees?size=${pageSize}`, {
method: 'GET',
headers: new Headers({'Content-Type': 'application/json',
'Authorization': 'Basic '+btoa('greg:turnquist'))})


all works, but this looks silly after all effort put in configuring
Spring Security
beans
and for sure it is not the best practice.

All java sources remain the same as in tutorial but I include fragments from configuration classes here:

SecurityConfiguration.java


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
private SpringDataJpaUserDetailsService userDetailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.userDetailsService)
.passwordEncoder(Manager.PASSWORD_ENCODER);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/build/**", "/main.css").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/", true)
.permitAll()
.and()
.httpBasic()
.and()
.csrf().disable()
.logout()
.logoutSuccessUrl("/");
}

}


SpringDataJpaUserDetailsService.java


@Component
public class SpringDataJpaUserDetailsService implements UserDetailsService {

private final ManagerRepository repository;

@Autowired
public SpringDataJpaUserDetailsService(ManagerRepository repository) {
this.repository = repository;
}

@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
Manager manager = this.repository.findByName(name);
return new User(manager.getName(), manager.getPassword(),
AuthorityUtils.createAuthorityList(manager.getRoles()));
}

}


EmployeeRepository.java


@PreAuthorize("hasRole('ROLE_MANAGER')")
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {

@Override
@PreAuthorize("#employee?.manager == null or #employee?.manager?.name == authentication?.name")
Employee save(@Param("employee") Employee employee);

@Override
@PreAuthorize("@employeeRepository.findOne(#id)?.manager?.name == authentication?.name")
void delete(@Param("id") Long id);

@Override
@PreAuthorize("#employee?.manager?.name == authentication?.name")
void delete(@Param("employee") Employee employee);

}


So my question is how to send credential from authorized page using new
javascript
fetch API to interact with REST repositories without additional security configuration?

Answer

The root of the problem is that fetch() doesn't send cookies by default. So I should add credentials: 'include' to request options object:

fetch(`${root}/employees?size=${pageSize}`, {
  method: 'GET',
  credentials: 'include'})

references