How to prevent NestedServletException when testing Spring endpoints?

六月ゝ 毕业季﹏ 提交于 2019-12-04 17:43:26

问题


I am trying to test the security configuration of some of my endpoints which are secured with @PreAuthorize(#oauth2.hasScope('scope'). When accessing such an endpoint via Postman with a access token that does not have the required scope, the following is returned with HTTP status code 403 (forbidden):

{
    "error": "insufficient_scope",
    "error_description": "Insufficient scope for this resource",
    "scope": "scope"
}

Which is the expected behaviour that I want.

When trying to test this configuration, Springs NestedServletException interferes with my test case before it can complete with my expected result.

This is a simplified version of the controller I want to test:

@RestController
@RequestMapping(value = "/api")
public class OauthTestingResource {

    @PreAuthorize(#oauth2.hasScope('scope'))
    @RequestMapping(value = "/scope", method = RequestMethod.GET)
    public void endpoint() {
        // ...
    }
}

And this is the corresponding test case:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MyApplication.class)
@WebAppConfiguration
public class AuthorizationTest {

    @Autowired
    protected WebApplicationContext webApplicationContext;

    protected SecurityContext securityContext = Mockito.mock(SecurityContext.class);

    @Before
    public void setup() throws Exception {

        this.mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        SecurityContextHolder.setContext(securityContext);
    }

    protected Authentication createMockAuth(Client client) {

        final List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

        final Authentication pwAuth = new UsernamePasswordAuthenticationToken("testuser", "testpw", authorities);

        final TokenRequest request = new TokenRequest(new HashMap<>(), client.getClientId(), client.getScopes(), "password");

        final OAuthClient oauthClient = new OAuthClient(client, GrantType.PASSWORD);

        return new OAuth2Authentication(request.createOAuth2Request(oauthClient), pwAuth);
    }
    @Test
    public void testAppScope() throws Exception {

        final Client client = new Client("id1", "secret1");

        client.setScope("scope");
        Mockito.when(securityContext.getAuthentication()).thenReturn(createMockAuth(client));
        // this test passes
        mvc.perform(get("/api/scope")).andExpect(status().isOk()); 

        client.setScope("other_scope");
        Mockito.when(securityContext.getAuthentication()).thenReturn(createMockAuth(client));
        // NestedServletException thrown here
        mvc.perform(get("/api/scope")).andExpect(status().isForbidden()); 
    }
}

The exception that is thrown is the following (which is expected):

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.security.access.AccessDeniedException: Insufficient scope for this resource

My question is how can I prevent this exception from interfering with my test case?


回答1:


I did spring security test cases by following this link. Things worked fine except this issue of nesting original exception in NestedServletException. I did not find any direct way to figure this out but AspectJ helped me in handling this in a cleaner way.

We can use the static assertThatThrownBy() method of the Assertions class. This method returns an AbstractThrowableAssert object that we can use to write assertions for the thrown exception.

The code that captures an exception thrown by the methodThatThrowsException() method looks as follows:

assertThatThrownBy(() -> methodThatThrowsException())
.isExactlyInstanceOf(DuplicateEmailException.class);

Thanks to this excellent blog where you can find additional details.

The way in which I handled this in my test case would be (by taking your test case codeline):

org.assertj.core.api.Assertions.assertThatThrownBy(() -> mvc.perform(get("/api/scope")).andExpect(status().isOk())).hasCause(new AccessDeniedException("Access is denied"));

That way your test case would be able to assert actual AccessDeniedException that is nested in NestedServletException.




回答2:


I fixed it by adding an @ExceptionHandler for that exception. Seems like if MockMvc throws an actual exception it means you don't "handle" this case which is not ideal.



来源:https://stackoverflow.com/questions/44900646/how-to-prevent-nestedservletexception-when-testing-spring-endpoints

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!