如何在春季云合同测试中模拟Principal?

时间:2019-09-16 13:44:58

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

在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全新安装,我有下一个异常:

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

正确的模拟主体和通过测试我需要做什么?

2 个答案:

答案 0 :(得分:0)

设置基类并放心使用时,您需要使用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作为标准名称传递。

答案 1 :(得分:0)

我的项目使用Oauth2安全性。

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

具有OAuth2AuthenticationDetails Bean的类配置为:

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();
    }
}