如何使用oauth2范围测试Spring PreAuthorize

时间:2019-02-14 09:34:47

标签: spring-boot oauth-2.0 integration-testing

我们有一个受ResourceServer保护的应用程序,并且我们正在使用GlobalMethodSecurity来限制客户端范围对API端点的访问。一切都按预期进行。

我现在正在尝试编写一些集成测试。在我的测试中,我手动创建一个有效的JWT,然后调用受限端点。如果我没有发送令牌,则会如预期的那样得到401,但是当我发送缺少必需范围的令牌时,它不会被拒绝,但能够到达端点。

我想我缺少使@PreAuthorize正常工作的一些配置或设置,但是我不确定到底缺少什么。

这是我的考试班:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest
public class UserIT extends BaseIntegrationTest {

@Autowired
private UserRepository userRepository;

@Autowired
private WebApplicationContext wac;

@Autowired
private FilterChainProxy springSecurityFilterChain;

private MockMvc mockMvc;

@Before
public void setup () {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build();
}

@Test
public void cannotGetUsersWithoutToken () throws Exception {
    mockMvc
            .perform(get("/user/protected/user"))
            .andExpect(status().isUnauthorized());
}

@Test
public void cannotGetUsersWithoutCorrectScope () throws Exception {
    String token = getToken(Collections.singletonList("MY_ROLE"), 2);

    mockMvc
            .perform(get("/user/protected/user").header("Authorization", "Bearer " + token))
            .andExpect(status().isUnauthorized());
    } 
}

这是API方法:

@PreAuthorize("isAuthenticated() and #oauth2.hasScope('my-scope')")
@RequestMapping(value="/user", method = RequestMethod.GET)
public UserInfo getUser(){
    long userId = getUserId();

    return userService.retrieveUser(userId);
}

第一个测试通过,但第二个失败;返回200而不是401

关于我所缺少的任何建议将不胜感激。我一直在关注这个https://www.baeldung.com/oauth-api-testing-with-spring-mvc

1 个答案:

答案 0 :(得分:2)

答案在这里How to get @WebMvcTest work with OAuth?

您只需要创建一个界面即可在所需范围内模拟PreAuthorize。

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockOAuth2Client.WithMockOAuth2ClientSecurityContextFactory.class)
public @interface WithMockOAuth2Client {

    String clientId() default "web-client";

    String[] scope() default {"openid"};

    String[] authorities() default {};

    boolean approved() default true;

    class WithMockOAuth2ClientSecurityContextFactory implements WithSecurityContextFactory<WithMockOAuth2Client> {

        public static OAuth2Request getOAuth2Request(final WithMockOAuth2Client annotation) {
            final Set<? extends GrantedAuthority> authorities = Stream.of(annotation.authorities())
                    .map(auth -> new SimpleGrantedAuthority(auth))
                    .collect(Collectors.toSet());

            final Set<String> scope = Stream.of(annotation.scope())
                    .collect(Collectors.toSet());

            return new OAuth2Request(
                    null,
                    annotation.clientId(),
                    authorities,
                    annotation.approved(),
                    scope,
                    null,
                    null,
                    null,
                    null);
        }

        @Override
        public SecurityContext createSecurityContext(final WithMockOAuth2Client annotation) {
            final SecurityContext ctx = SecurityContextHolder.createEmptyContext();
            ctx.setAuthentication(new OAuth2Authentication(getOAuth2Request(annotation), null));
            SecurityContextHolder.setContext(ctx);
            return ctx;
        }
    }
}

然后将它们用作测试

@Test
@WithMockOAuth2Client(scope={ "your-scope" })
...