简体   繁体   English

测试时似乎没有应用 spring-security 的配置

[英]Configuration of spring-security doesn't seem to be applied while testing

I have configured a standalone mockMvc with a corresponding xml security config for testing my controller that is annotated with @PreAuthorize(hasAnyRole('DM,CD')).我已经使用相应的 xml 安全配置配置了一个独立的 mockMvc,用于测试我用 @PreAuthorize(hasAnyRole('DM,CD')) 注释的控制器。 But the result of my tests always is tatus 200, even though I make a call with a user with incorrect role.但是我的测试结果总是 tatus 200,即使我打电话给一个角色不正确的用户。 I believe that my xml config misses something, but it's just a assumption.我相信我的 xml 配置遗漏了一些东西,但这只是一个假设。

Here is my controller I want to test:这是我要测试的控制器:

@RestController
@RequestMapping(value = "/v1/view")
@PreAuthorize("hasAnyRole('Data Manager,Content Designer')")
public class ViewController {
    ObjectMapper objectMapper = new JSONMapper();

    @Autowired
    ViewApiService viewApiService;

    //Constructor is needed for tests
    ViewController(ViewApiService viewApiService) {
        this.viewApiService = viewApiService;
    }

    public ViewController() {
    }

    @TruncateLogData
    @RequestMapping(method = RequestMethod.GET)
    public List<View> getViewItems() throws GenericEngineException {
        return viewApiService.getViews();
    }
}

Here is test spring-security.xml borrowed from main configuration:这是从主配置中借用的测试 spring-security.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.2.xsd">


    <!-- ****** START Spring Security Configuration *******-->
    <bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
        <constructor-arg>
            <list>
                <security:filter-chain pattern="/**" filters="
                    httpSessionContextIntegrationFilter,
                    logoutFilter,
                    basicAuthenticationProcessingFilter,
                    basicExceptionTranslationFilter,
                    servletApiBridge,
                    filterSecurityInterceptor"/>
            </list>
        </constructor-arg>
    </bean>

    <!-- ****** START Spring Filters *******-->

    <!-- Session integration filter -->
    <bean id="httpSessionContextIntegrationFilter"
          class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
        <constructor-arg>
            <bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
                <property name='allowSessionCreation' value='false'/>
            </bean>
        </constructor-arg>
    </bean>


    <!-- Basic Authentication processing filter -->
    <bean id="basicAuthenticationProcessingFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
        <constructor-arg ref="authenticationManager"/>
        <constructor-arg ref="basicAuthenticationEntryPoint"/>
    </bean>

    <bean id="basicAuthenticationEntryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
        <property name="realmName" value="restapi"/>
    </bean>

    <!-- Basic Exception translation filter -->
    <bean id="basicExceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <constructor-arg ref="basicAuthenticationEntryPoint"/>
    </bean>


    <!-- Security Context Holder Aware Request filter
     Filter that passes wrapped HttpServletRequest which overrides getUserPrincipal() to return current Authentication
    -->
    <bean id="servletApiBridge" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>

    <!-- Logout filter
     Filter that logs the user out in SpringSecurity
    -->
    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg value="/" index="0"/>
        <constructor-arg index="1">
            <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
        </constructor-arg>
    </bean>

    <!-- ****** END Spring Filters *******-->

    <!-- ****** START Spring Security interceptor config *******-->
     <!--Define authentication manager, decision manager and secure URL patterns -->
    <bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
        <property name="securityMetadataSource">
            <security:filter-security-metadata-source>
                <security:intercept-url pattern="/api/**" access="ROLE_ADMIN"/>
            </security:filter-security-metadata-source>
        </property>
    </bean>
    <!-- ****** END Spring Security interceptor config *******-->

    <!-- ****** START Spring authentication config *******-->
    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
        <constructor-arg>
            <list>
                <ref bean="usernamePasswordAuthenticationProvider"/>
            </list>
        </constructor-arg>

    </bean>


    <!-- Custom UsernamePassword Authentication Provider -->
    <bean id="usernamePasswordAuthenticationProvider" class="com.custom.UsernamePasswordAuthenticationProvider">
        <property name="adminLogin" value="true"/>
        <property name="customLoginFacade" ref="userSessionFactoryFacade"/>
        <property name="additionalRole" value="ROLE_ADMIN"/>
        <property name="userInterfaceType" value="API"/>
    </bean>

    <!-- ****** END Spring authentication config *******-->

    <!-- ****** START Spring authorization config *******-->
    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
        <constructor-arg>
            <list>
                <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
            </list>
        </constructor-arg>
    </bean>
    <!-- ****** END Spring authorization config *******-->


    <!-- ****** END Spring Security Configuration *******-->
    <bean id="userSessionFactoryFacade" class="com.custom.CustomLoginFacadeImpl"/>

</beans>

Here is my test for controller:这是我对控制器的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/testContextConfig.xml", "/spring-security.xml"})
@WebAppConfiguration
public class ViewControllerTest {
    private MockMvc mockMvc;
    private List<View> viewList;

    @Mock
    private ViewApiService viewApiService;

    @Autowired
    private Filter springSecurityFilterChain;

    @Before
    public void setup() throws JSONException {
        MockitoAnnotations.initMocks(this);

        mockMvc = MockMvcBuilders
                .standaloneSetup(new ViewController(viewApiService))
                .addFilter(springSecurityFilterChain)
                .setHandlerExceptionResolvers(TestServletExceptionHandler.exceptionResolver())
                .build();

        viewList = new ArrayList<>();

        //initialization of resource to get
        viewList.add(new View("1", "view1", content1));
    }



    @Test
    @WithMockUser(username = "testUser3", roles = {"DM"})
    public void testIncorrectAccessToControllersMethods() throws Exception {
            when(viewApiService.getViews()).thenReturn(viewList);
            mockMvc.perform(get("/v1/view"))
                    .andExpect(status().isOk())
                    .andReturn().getResponse().getContentAsString();
            verify(viewApiService, times(1)).getViews();
    }

    @Test
    @WithMockUser(username = "testUser4", roles = {"OTHER_ROLE"})
    public void testDMAccessToControllersMethods() throws Exception {
         when(viewApiService.getViews()).thenReturn(viewList);
         mockMvc.perform(get("/v1/view"))
                    .andExpect(status().isUnauthorized());             
         verify(viewApiService, times(0)).getViews();
    }

But as a result I get但结果我得到

ViewControllerTest.testIncorrectAccessToControllersMethods Status expected:<401> but was:<200>

testContextConfig.xml file is just an empty context. testContextConfig.xml 文件只是一个空上下文。

Any advice will be appreciated任何建议将不胜感激

Update Creation of ViewControllerInterface with corresponding annotations on it didn't help, as well as adding使用相应的注释更新ViewControllerInterface 的创建没有帮助,以及添加

 <global-method-security pre-post-annotations="enabled" />

to config file到配置文件

Update 2 Interesting is that when I try to construct call with mockMvc.perform(get("/v1/view").with(user("name"))).andReturn().getResponse()更新 2有趣的是,当我尝试使用mockMvc.perform(get("/v1/view").with(user("name"))).andReturn().getResponse()构造调用时

I receive我收到

NoSuchMethodError: javax.servlet.http.HttpServletRequest.getServletContext()Ljavax/servlet/ServletContext;
        at org.springframework.security.test.web.support.WebTestUtils.findFilter(WebTestUtils.java:120)

So it looks like my securityFilterChain wasn't applied for a request (however, in debug I can see all the filters in mockMvc instance)所以看起来我的 securityFilterChain 没有应用于请求(但是,在调试中我可以看到 mockMvc 实例中的所有过滤器)

After few hours of digging here is why: MockMvcBuilders.standaloneSetup get ViewController instantiated manually (not with Spring and therefore not with AOP).经过几个小时的挖掘,原因如下: MockMvcBuilders.standaloneSetup手动实例化ViewController (不是使用 Spring,因此不是使用 AOP)。 Therefore the PreAuthorize is not intercepted and security check is skipped.因此不会拦截 PreAuthorize 并跳过安全检查。 You can therefore either @Autowire your controller and pass it to MockMvcBuilders.standaloneSetup to mock your services use @MockBean so that every instance of ViewApiService gets replaced with the Mock one.因此,您可以@Autowire控制器,并把它传递给MockMvcBuilders.standaloneSetup嘲笑你的服务使用@MockBean ,这样的每个实例ViewApiService获取与模拟替代。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM