【问题标题】:How to mock Principal in spring cloud contract tests?如何在 Spring Cloud 合约测试中模拟 Principal?
【发布时间】:2020-01-17 08:54:24
【问题描述】:

在 REST 控制器中,我有几种方法需要创建合同测试,但我不知道如何提供 Principal 以通过测试。

Controller 中的一种方法,其参数为 Principal:

    @PreAuthorize("hasRole('USER')")
    @GetMapping("/current")
    public Details getCurrent(Principal principal) {
        return houseManager.getById(Principals.getCurrentUserId(principal));
    }

我已经为测试创建了基类:

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = {Controller.class})
@ContextConfiguration(classes = {TestConfig.class, ControllerTestConfig.class})
@ComponentScan(basePackageClasses = {Controller.class})
@AutoConfigureStubRunner
public class ControllersWithSecurityBase {
    @Autowired 
    privet Service service;
    @Autowired
    WebApplicationContext context;
    @Mock
    private Principal mockedPrincipal;

    RestAssuredMockMvc.standaloneSetup(new Controller(service));
    RequestBuilder requestBuilder = MockMvcRequestBuilders
                .get("/") 
                .with(user("user")
                        .password("password")
                        .roles("USER"))
                .principal(mockedPrincipal)
                .accept(MediaType.APPLICATION_JSON);
        MockMvc mockMvc = MockMvcBuilders
                .webAppContextSetup(context)
                .defaultRequest(requestBuilder)
                .apply(springSecurity())
                .build();
        RestAssuredMockMvc.mockMvc(mockMvc);
}

合同:

Contract.make {
    name("Should find current by principal")
    request {
        method(GET)
        urlPath(".../current")
    }

    response {
        status(200)
    }
}

由于 mvn clean install 我有下一个异常:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: org.springframework.security.authentication.UsernamePasswordAuthenticationToken cannot be cast to java.util.Map

我需要做什么才能正确地模拟 Principal 并通过测试?

【问题讨论】:

  • @WithMockUser(...)
  • Zorglube,你能不能更深入地解释一下?因为我已经尝试将它注释为:@WithMockUser(username = "user", roles = "USER") 以上 setUp 方法和以上类 - 结果它仍然无法正常工作。
  • @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = Application.class) @AutoConfigureMockMvc @Transactional @RunWith(value = SpringRunner.class) public class Tests { @Test @WithMockUser(username = "user_test", authorities = "ROLE_USER") public void test() { ... } }

标签: spring-mvc spring-security spring-cloud


【解决方案1】:

当您设置基类并放心时,您需要使用 AuthenticationPrincipalArgumentResolver 传递自定义参数解析器

        mockMvc(
                standaloneSetup( controller )
                        .setCustomArgumentResolvers( new AuthenticationPrincipalArgumentResolver() )

您也可以传入将在每个测试中添加 WithMockUser 的插件

<build>
        <plugin>
            <groupId>com.google.code.maven-replacer-plugin</groupId>
            <artifactId>maven-replacer-plugin</artifactId>
            <version>${maven-replacer-plugin.version}</version>
            <executions>
                <execution>
                    <phase>generate-test-sources</phase>
                    <goals>
                        <goal>replace</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <includes>
                    <include>target/generated-test-sources/**/*.java</include>
                </includes>
                <regex>true</regex>
                <regexFlags>
                    <regexFlag>MULTILINE</regexFlag>
                </regexFlags>
                <replacements>
                    <replacement>
                        <token>@Test</token>
                        <value>@WithMockUser
@Test</value>
                    </replacement>
                </replacements>
            </configuration>
        </plugin>
    </plugins>
</build>

记得将 WithMockUser 作为完全限定名称传递。

【讨论】:

  • 嗨,Marcin,感谢您的回答。当我添加new AuthenticationPrincipalArgumentResolver() 时,它的构造函数需要在参数中设置 ReactiveAdapterRegistry。如果我创建此类的实例并将其设置为参数 - 结果出现错误无法解析方法 setCustomArgumentResolvers。如何解决方法.setCustomArgumentResolvers( new AuthenticationPrincipalArgumentResolver())
  • 您很可能遇到版本不匹配的问题
  • 好的,请告诉我,我需要哪些依赖项和版本?
  • 如果我理解正确的话,我不放心,BOM 是这样的父母,是吗? &lt;parent&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt; &lt;version&gt;2.1.6.RELEASE&lt;/version&gt; &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt; &lt;/parent&gt;
【解决方案2】:

我的项目使用 Oauth2 安全性。

所以为了在合同测试中模拟 Principal 对象,我创建了 OAuth2AuthenticationDetails 的 bean(这个类实现了 Principal 接口)。

带有 bean OAuth2AuthenticationDetails 的类配置是:

package com.example.config.fakesecurityconfig;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;

import java.util.*;

@Configuration
public class OAuth2TestConfig {

    @Bean
    public OAuth2Authentication oAuth2Authentication() {
        return new OAuth2Authentication(getStoredRequest(), getUserAuthentication());
    }

    @Bean
    public OAuth2AuthenticationDetails oAuth2AuthenticationDetails() {
        MockHttpServletRequest request = new MockHttpServletRequest();
        return new OAuth2AuthenticationDetails(request);
    }

    private OAuth2Request getStoredRequest() {
        Set<String> scope = new HashSet<>();
        scope.add("read");
        scope.add("write");
        return new OAuth2Request(
                Collections.EMPTY_MAP,
                "clientId",
                getGrantedAuthorityCollection(),
                true,
                scope,
                Collections.EMPTY_SET,
                null,
                Collections.EMPTY_SET,
                Collections.EMPTY_MAP);
    }

    private Authentication getUserAuthentication() {
        String credentials = "PROTECTED";
        Authentication authentication = new TestingAuthenticationToken(getPrincipalMap(), credentials, getGrantedAuthorityAsList());
        return new OAuth2Authentication(getStoredRequest(), authentication);
    }

    private Map<String, String> getPrincipalMap() {
        Map<String, String> principalMap = new LinkedHashMap<>();
        principalMap.put("id", "5c49c98d3a0f3a23cd39a720");
        principalMap.put("username", "TestUserName");
        principalMap.put("password", "TestPassword");
        principalMap.put("createdAt", "2018-06-14 10:35:05");
        principalMap.put("userType", "USER");
        principalMap.put("authorities", getGrantedAuthorityCollectionAsMap().toString());
        principalMap.put("accountNonExpired", "true");
        principalMap.put("accountNonLocked", "true");
        principalMap.put("credentialsNonExpired", "true");
        principalMap.put("enabled", "true");
        principalMap.put("uniqueId", "null");
        principalMap.put("uniqueLink", "fc3552f4-0cdf-494d-bc46-9d1e6305400a");
        principalMap.put("uniqueLinkCreatedAt", "2019-09-06 10:44:36");
        principalMap.put("someId", "59b5a82c410df8000a83a1ff");
        principalMap.put("otherId", "59b5a82c410df8000a83a1ff");
        principalMap.put("name", "TestName");
        return principalMap;
    }

    private Collection<GrantedAuthority> getGrantedAuthorityCollection() {
        return Arrays.asList(
                new SimpleGrantedAuthority("ROLE_ADMIN"),
                new SimpleGrantedAuthority("ROLE_USER")
        );
    }

    private List<GrantedAuthority> getGrantedAuthorityAsList() {
        return new ArrayList<>(getGrantedAuthorityCollection());
    }

    private LinkedHashMap<String, GrantedAuthority> getGrantedAuthorityCollectionAsMap() {
        LinkedHashMap<String, GrantedAuthority> map = new LinkedHashMap<>();
        for (GrantedAuthority authority : getGrantedAuthorityCollection()) {
            map.put("authority", authority);
        }
        return map;
    }
}

因此,我的合同测试基类是:

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = {Controller.class})
@ContextConfiguration(classes = {TestConfig.class, OAuth2TestConfig.class})
@ComponentScan(basePackageClasses = {Controller.class})
@AutoConfigureStubRunner
@WebAppConfiguration
public abstract class HousesControllersSecuredBase {
    @Autowired
    private Service service;
    @Autowired
    private WebApplicationContext context;
    @Autowired
    private OAuth2Authentication oAuth2Authentication;
    @Autowired
    private OAuth2AuthenticationDetails oAuth2AuthenticationDetails;
    @Autowired
    private MockMvc mockMvc;

    @Before
    public void settingUpTests() {
        RestAssuredMockMvc.standaloneSetup(Controller(houseService));
        mockMvc = MockMvcBuilders
                .webAppContextSetup(context)
                .build();
        RestAssuredMockMvc.mockMvc(mockMvc);
        oAuth2Authentication.setDetails(oAuth2AuthenticationDetails);
        RestAssuredMockMvc.authentication = 
            RestAssuredMockMvc.principal(oAuth2Authentication);
    }

    @After
    public void ShuttingDownTests() {
        RestAssuredMockMvc.reset();
    }
}

【讨论】:

    猜你喜欢
    • 2019-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多