1

Problem: when i try testing authenticate-required method(with MockUser) it's return 403 error, i missing something, a try several approach, but they does't work. I don't understand why this happening so, can someone explain?

For example i create simple application to demonstrate this.

Spring-security configuration class

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SpringConfigure extends WebSecurityConfigurerAdapter {


@Override
protected void configure(HttpSecurity http) throws Exception {
    configureAuthorization(http);
    configureAuthentication(http);
     }



private void configureAuthorization(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/api/**").authenticated();
    }


private void configureAuthentication(HttpSecurity http) throws Exception {
    AuthenticationEntryPoint authenticationEntryPoint = (request, response, e) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
    }
}

Controller class

@RestController
@RequestMapping("api/hello")
public class HelloController {

@RequestMapping(method = RequestMethod.GET)
public String hello(){
    return "Hello";
   }
}

and my test class

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DemoApplication.class},webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class DemoApplicationTests {

RestTemplate restTemplate;

@Before
public void setUp(){
    restTemplate = new RestTemplate();
   }

@Test
@WithMockUser(username = "user")
public void helloTest() {
    System.out.println(SecurityContextHolder.getContext().getAuthentication());
    ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://localhost:8080/api/hello",String.class);
   }
}

User created by @WithMockUser

Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER
Alexey.S
  • 129
  • 4

2 Answers2

0

I run your test code and it seems OK.

@Test
@WithMockUser(username = "user")
public void helloTest() {
System.out.println(SecurityContextHolder.getContext().getAuthentication());
    ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://localhost:8080/api/hello",String.class);
   }
}
System.out.println(responseEntity);

Result:

org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ca25360: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER
<200,Hello
renqHIT
  • 174
  • 1
  • 10
  • You run with Spring-security configuration class? Because it still does not work(work only if i change authenticated() to permitAll()), at first I thought it was probably a problem in my environment and try to run it on my home computer, but nothing, the result was the same. I do not know, maybe it's something in gradle or something "exotic". – Alexey.S Feb 19 '18 at 06:07
0

After a lot of time i have found the answer here https://stackoverflow.com/a/15203488/8664578 (thx a lot), and repeating this answer(https://stackoverflow.com/a/23658269/8664578) - easiest way to test authenticate-required method that it's:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DemoApplication.class},webEnvironment = 
SpringBootTest.WebEnvironment.DEFINED_PORT)

@Autowired
private WebApplicationContext context;
@Before
public void setup() {
    mvc = MockMvcBuilders
          .webAppContextSetup(context)
          .apply(springSecurity())  
          .build();
   }

public class DemoApplicationTests {
@Test
@WithMockUser
public void helloTest() {
mockMvc.perform(MockMvcRequestBuilders.get("http://localhost:8080/api/hello")).andExpect(status().isOk());
    }
}

But in the beginning my question was how do that with RestTemplate, and i use solution that was proposed in the post with answer just slightly changed it (does't best, but solution). According to the link above:

"The HttpSessionSecurityContextRepository inspects the given HttpRequest and tries to access the corresponding HttpSession. If it exists, it will try to read the SecurityContext from the HttpSession. If this fails, the repository generates an empty SecurityContext"

We can add a filter to filterChain, which will add the session attribute with the Spring context that we want, as in the example below:

public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("user","password", 
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
   SecurityContextHolder.getContext().setAuthentication(authenticationToken);

    HttpServletRequest httpServletRequest = (HttpServletRequest) request;

    HttpSession session = httpServletRequest.getSession();
    session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
            SecurityContextHolder.getContext());

    chain.doFilter(request,response);
}

@Override
public void destroy() {

   }
}

Also we need to add filter to @Configuration class like below:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity
public class SpringConfigure extends WebSecurityConfigurerAdapter {


@Override
protected void configure(HttpSecurity http) throws Exception {
  configureAuthorization(http);
  configureAuthentication(http);
 }



private void configureAuthorization(HttpSecurity http) throws Exception {
  http.authorizeRequests().antMatchers("/api/**").authenticated().and().addFilterBefore(new CustomFilter(), SecurityContextPersistenceFilter.class);
}


private void configureAuthentication(HttpSecurity http) throws Exception {
AuthenticationEntryPoint authenticationEntryPoint = (request, response, e) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
  }
}

Finally test class:

@Test
public void helloTest() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://localhost:8080/api/hello",String.class);
  }
}

Hope it's will help for someone.

Alexey.S
  • 129
  • 4