การทดสอบและความปลอดภัยในฤดูใบไม้ผลิ: วิธีจำลองการตรวจสอบสิทธิ์


124

ฉันพยายามหาวิธีทดสอบหน่วยว่า URL ของคอนโทรลเลอร์ของฉันมีการรักษาความปลอดภัยอย่างเหมาะสมหรือไม่ ในกรณีที่มีคนเปลี่ยนแปลงสิ่งต่างๆรอบตัวและลบการตั้งค่าความปลอดภัยโดยไม่ได้ตั้งใจ

วิธีการควบคุมของฉันมีลักษณะดังนี้:

@RequestMapping("/api/v1/resource/test") 
@Secured("ROLE_USER")
public @ResonseBody String test() {
    return "test";
}

ฉันตั้งค่า WebTestEnvironment ดังนี้:

import javax.annotation.Resource;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({ 
        "file:src/main/webapp/WEB-INF/spring/security.xml",
        "file:src/main/webapp/WEB-INF/spring/applicationContext.xml",
        "file:src/main/webapp/WEB-INF/spring/servlet-context.xml" })
public class WebappTestEnvironment2 {

    @Resource
    private FilterChainProxy springSecurityFilterChain;

    @Autowired
    @Qualifier("databaseUserService")
    protected UserDetailsService userDetailsService;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    protected DataSource dataSource;

    protected MockMvc mockMvc;

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    protected UsernamePasswordAuthenticationToken getPrincipal(String username) {

        UserDetails user = this.userDetailsService.loadUserByUsername(username);

        UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(
                        user, 
                        user.getPassword(), 
                        user.getAuthorities());

        return authentication;
    }

    @Before
    public void setupMockMvc() throws NamingException {

        // setup mock MVC
        this.mockMvc = MockMvcBuilders
                .webAppContextSetup(this.wac)
                .addFilters(this.springSecurityFilterChain)
                .build();
    }
}

ในการทดสอบจริงของฉันฉันพยายามทำสิ่งนี้:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class CopyOfClaimTest extends WebappTestEnvironment {

    @Test
    public void signedIn() throws Exception {

        UsernamePasswordAuthenticationToken principal = 
                this.getPrincipal("test1");

        SecurityContextHolder.getContext().setAuthentication(principal);        

        super.mockMvc
            .perform(
                    get("/api/v1/resource/test")
//                    .principal(principal)
                    .session(session))
            .andExpect(status().isOk());
    }

}

ฉันเลือกสิ่งนี้ที่นี่:

แต่หากมีใครตรวจสอบอย่างใกล้ชิดสิ่งนี้จะช่วยได้ก็ต่อเมื่อไม่ได้ส่งคำขอจริงไปยัง URL แต่เฉพาะเมื่อทดสอบบริการในระดับฟังก์ชันเท่านั้น ในกรณีของฉันเกิดข้อยกเว้น "การเข้าถึงถูกปฏิเสธ":

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.1.RELEASE.jar:3.2.1.RELEASE]
        ...

ข้อความบันทึกสองข้อความต่อไปนี้เป็นสิ่งที่น่าสังเกตโดยทั่วไปว่าไม่มีการรับรองความถูกต้องของผู้ใช้บ่งชี้ว่าการตั้งค่าPrincipalไม่ทำงานหรือถูกเขียนทับ

14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List test.TestController.test(); target is of class [test.TestController]; Attributes: [ROLE_USER]
14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS

ชื่อ บริษัท ของคุณ eu.ubicon จะปรากฏในการนำเข้าของคุณ นั่นไม่ใช่ความเสี่ยงด้านความปลอดภัยหรือ?
Kyle Bridenstine

2
สวัสดีขอบคุณสำหรับความคิดเห็น! ฉันไม่เห็นว่าทำไม มันเป็นซอฟต์แวร์โอเพนซอร์สอยู่ดี หากคุณสนใจโปรดดูbitbucket.org/ubicon/ubicon (หรือbitbucket.org/dmir_wue/everyawareสำหรับส้อมล่าสุด) แจ้งให้เราทราบหากพลาดอะไรไป
Martin Becker

ตรวจสอบโซลูชันนี้ (คำตอบสำหรับฤดูใบไม้ผลิ 4): stackoverflow.com/questions/14308341/…
Nagy Attila

คำตอบ:


101

การค้นหาคำตอบฉันไม่สามารถหาคำตอบได้ง่ายและยืดหยุ่นในเวลาเดียวกันจากนั้นฉันพบSpring Security Referenceและฉันรู้ว่ามีโซลูชันที่สมบูรณ์แบบอยู่ใกล้ โซลูชัน AOP มักเป็นโซลูชันที่ยอดเยี่ยมที่สุดสำหรับการทดสอบและ Spring จัดเตรียมไว้@WithMockUserให้@WithUserDetailsและ@WithSecurityContextในสิ่งประดิษฐ์นี้:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.2.2.RELEASE</version>
    <scope>test</scope>
</dependency>

ในกรณีส่วนใหญ่@WithUserDetailsรวบรวมความยืดหยุ่นและพลังที่ฉันต้องการ

@WithUserDetails ทำงานอย่างไร

โดยพื้นฐานแล้วคุณต้องสร้างแบบกำหนดเองUserDetailsServiceด้วยโปรไฟล์ผู้ใช้ที่เป็นไปได้ทั้งหมดที่คุณต้องการทดสอบ เช่น

@TestConfiguration
public class SpringSecurityWebAuxTestConfig {

    @Bean
    @Primary
    public UserDetailsService userDetailsService() {
        User basicUser = new UserImpl("Basic User", "user@company.com", "password");
        UserActive basicActiveUser = new UserActive(basicUser, Arrays.asList(
                new SimpleGrantedAuthority("ROLE_USER"),
                new SimpleGrantedAuthority("PERM_FOO_READ")
        ));

        User managerUser = new UserImpl("Manager User", "manager@company.com", "password");
        UserActive managerActiveUser = new UserActive(managerUser, Arrays.asList(
                new SimpleGrantedAuthority("ROLE_MANAGER"),
                new SimpleGrantedAuthority("PERM_FOO_READ"),
                new SimpleGrantedAuthority("PERM_FOO_WRITE"),
                new SimpleGrantedAuthority("PERM_FOO_MANAGE")
        ));

        return new InMemoryUserDetailsManager(Arrays.asList(
                basicActiveUser, managerActiveUser
        ));
    }
}

ตอนนี้เรามีผู้ใช้พร้อมแล้วลองนึกดูว่าเราต้องการทดสอบการควบคุมการเข้าถึงฟังก์ชันคอนโทรลเลอร์นี้:

@RestController
@RequestMapping("/foo")
public class FooController {

    @Secured("ROLE_MANAGER")
    @GetMapping("/salute")
    public String saluteYourManager(@AuthenticationPrincipal User activeUser)
    {
        return String.format("Hi %s. Foo salutes you!", activeUser.getUsername());
    }
}

ที่นี่เรามีฟังก์ชั่น get mappedไปยัง route / foo / saluteและเรากำลังทดสอบความปลอดภัยตามบทบาทด้วย@Securedคำอธิบายประกอบแม้ว่าคุณจะสามารถทดสอบ@PreAuthorizeและ@PostAuthorizeเช่นกัน มาสร้างการทดสอบสองแบบโดยรายการหนึ่งเพื่อตรวจสอบว่าผู้ใช้ที่ถูกต้องสามารถเห็นคำตอบนี้หรือไม่และอีกรายการหนึ่งเพื่อตรวจสอบว่ามีการห้ามจริงหรือไม่

@RunWith(SpringRunner.class)
@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = SpringSecurityWebAuxTestConfig.class
)
@AutoConfigureMockMvc
public class WebApplicationSecurityTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithUserDetails("manager@company.com")
    public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
                .accept(MediaType.ALL))
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("manager@company.com")));
    }

    @Test
    @WithUserDetails("user@company.com")
    public void givenBasicUser_whenGetFooSalute_thenForbidden() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
                .accept(MediaType.ALL))
                .andExpect(status().isForbidden());
    }
}

ตามที่คุณเห็นเรานำเข้าSpringSecurityWebAuxTestConfigเพื่อให้ผู้ใช้ของเราทำการทดสอบ แต่ละอันใช้ในกรณีทดสอบที่เกี่ยวข้องเพียงแค่ใช้คำอธิบายประกอบที่ตรงไปตรงมาลดโค้ดและความซับซ้อน

ใช้ @WithMockUser ดีกว่าสำหรับ Role Based Security ที่ง่ายขึ้น

อย่างที่คุณเห็น@WithUserDetailsมีความยืดหยุ่นทั้งหมดที่คุณต้องการสำหรับแอปพลิเคชันส่วนใหญ่ของคุณ ช่วยให้คุณสามารถใช้ผู้ใช้ที่กำหนดเองกับ GrantedAuthority เช่นบทบาทหรือสิทธิ์ แต่ถ้าคุณแค่ทำงานกับบทบาทการทดสอบอาจง่ายกว่าเดิมและคุณสามารถหลีกเลี่ยงการสร้างแบบกำหนดเองUserDetailsServiceได้ ในกรณีดังกล่าวระบุการรวมกันที่เรียบง่ายของผู้ใช้รหัสผ่านและบทบาทกับ@WithMockUser

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithSecurityContext(
    factory = WithMockUserSecurityContextFactory.class
)
public @interface WithMockUser {
    String value() default "user";

    String username() default "";

    String[] roles() default {"USER"};

    String password() default "password";
}

คำอธิบายประกอบกำหนดค่าดีฟอลต์สำหรับผู้ใช้พื้นฐาน ในกรณีของเราเส้นทางที่เรากำลังทดสอบเพียงแค่ต้องการให้ผู้ใช้ที่ได้รับการรับรองความถูกต้องเป็นผู้จัดการเราสามารถเลิกใช้SpringSecurityWebAuxTestConfigและทำสิ่งนี้ได้

@Test
@WithMockUser(roles = "MANAGER")
public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception
{
    mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
            .accept(MediaType.ALL))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("user")));
}

สังเกตว่าตอนนี้แทนที่จะเป็นผู้ใช้manager@company.comเราได้รับค่าเริ่มต้นจาก@WithMockUser: ผู้ใช้ ; แต่มันก็ไม่สำคัญเพราะสิ่งที่เราสนใจจริงๆคือบทบาทของเขา: ROLE_MANAGER.

สรุปผลการวิจัย

ดังที่คุณเห็นในคำอธิบายประกอบเช่น@WithUserDetailsและ@WithMockUserเราสามารถสลับไปมาระหว่างสถานการณ์ผู้ใช้ที่ได้รับการรับรองความถูกต้องต่างๆโดยไม่ต้องสร้างคลาสที่แปลกแยกจากสถาปัตยกรรมของเราเพียงเพื่อทำการทดสอบอย่างง่าย นอกจากนี้ยังแนะนำให้คุณดูว่า@WithSecurityContextทำงานอย่างไรเพื่อความยืดหยุ่นยิ่งขึ้น


จะล้อเลียนผู้ใช้หลายคนได้อย่างไร? ตัวอย่างเช่นคำขอแรกจะถูกส่งโดยtomในขณะที่คำขอที่สองคือjerry?
ch271828n

คุณสามารถสร้างฟังก์ชันที่การทดสอบของคุณใช้กับทอมและสร้างการทดสอบอื่นด้วยตรรกะเดียวกันและทดสอบกับเจอร์รี่ จะมีผลลัพธ์ที่เฉพาะเจาะจงสำหรับการทดสอบแต่ละครั้งดังนั้นจะมีการยืนยันที่แตกต่างกันและหากการทดสอบล้มเหลวจะมีการแจ้งชื่อผู้ใช้ / บทบาทที่ไม่ได้ผล โปรดจำไว้ว่าในคำขอผู้ใช้สามารถเป็นได้เพียงคนเดียวดังนั้นการระบุผู้ใช้หลายคนในคำขอจึงไม่สมเหตุสมผล
EliuX

ขออภัยฉันหมายถึงสถานการณ์ตัวอย่างเช่นเราทดสอบว่าทอมสร้างบทความลับจากนั้นเจอร์รี่พยายามอ่านสิ่งนั้นและเจอร์รี่ไม่ควรเห็น (เนื่องจากเป็นความลับ) ดังนั้นในกรณีนี้เป็นการทดสอบหนึ่งหน่วย ...
ch271828n

ดูเหมือนว่าสถานการณ์BasicUserและManager Userสถานการณ์ที่ให้ไว้ในคำตอบ แนวคิดหลักคือแทนที่จะใส่ใจผู้ใช้ที่เราสนใจบทบาทของพวกเขาจริง ๆ แต่การทดสอบแต่ละรายการที่อยู่ในการทดสอบหน่วยเดียวกันแทนคำค้นหาที่แตกต่างกัน ดำเนินการโดยผู้ใช้ที่แตกต่างกัน (ที่มีบทบาทต่างกัน) ไปยังจุดสิ้นสุดเดียวกัน
EliuX

61

ตั้งแต่ฤดูใบไม้ผลิ 4.0+ ทางออกที่ดีที่สุดคือใส่คำอธิบายประกอบวิธีการทดสอบด้วย @WithMockUser

@Test
@WithMockUser(username = "user1", password = "pwd", roles = "USER")
public void mytest1() throws Exception {
    mockMvc.perform(get("/someApi"))
        .andExpect(status().isOk());
}

อย่าลืมเพิ่มการอ้างอิงต่อไปนี้ในโครงการของคุณ

'org.springframework.security:spring-security-test:4.2.3.RELEASE'

1
ฤดูใบไม้ผลิเป็นสิ่งที่น่าอัศจรรย์ ขอบคุณ
TuGordoBello

คำตอบที่ดี. ยิ่งไปกว่านั้น - คุณไม่จำเป็นต้องใช้ mockMvc แต่ในกรณีที่คุณใช้เช่น PagingAndSortingRepository จาก springframework.data - คุณสามารถเรียกใช้เมธอดจากที่เก็บได้โดยตรง (ซึ่งมีคำอธิบายประกอบด้วย EL @PreAuthorize (...... ))
supertramp

50

ปรากฎว่าSecurityContextPersistenceFilterซึ่งเป็นส่วนหนึ่งของห่วงโซ่ตัวกรอง Spring Security จะรีเซ็ตของฉันเสมอSecurityContextซึ่งฉันตั้งค่าการโทรSecurityContextHolder.getContext().setAuthentication(principal)(หรือโดยใช้.principal(principal)วิธีการ) ตัวกรองนี้จะตั้งSecurityContextอยู่ในSecurityContextHolderที่มีSecurityContextจากSecurityContextRepository การเขียนทับหนึ่งที่ผมตั้งไว้ก่อนหน้านี้ ที่เก็บเป็นHttpSessionSecurityContextRepositoryค่าเริ่มต้น HttpSessionSecurityContextRepositoryตรวจสอบที่ได้รับและพยายามที่จะเข้าถึงที่สอดคล้องกันHttpRequest HttpSessionหากมีอยู่จะพยายามอ่านSecurityContextจากไฟล์HttpSession. SecurityContextหากล้มเหลวที่เก็บสร้างที่ว่างเปล่า

ดังนั้นวิธีแก้ปัญหาของฉันคือส่งHttpSessionไปพร้อมกับคำขอซึ่งถือSecurityContext:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class Test extends WebappTestEnvironment {

    public static class MockSecurityContext implements SecurityContext {

        private static final long serialVersionUID = -1386535243513362694L;

        private Authentication authentication;

        public MockSecurityContext(Authentication authentication) {
            this.authentication = authentication;
        }

        @Override
        public Authentication getAuthentication() {
            return this.authentication;
        }

        @Override
        public void setAuthentication(Authentication authentication) {
            this.authentication = authentication;
        }
    }

    @Test
    public void signedIn() throws Exception {

        UsernamePasswordAuthenticationToken principal = 
                this.getPrincipal("test1");

        MockHttpSession session = new MockHttpSession();
        session.setAttribute(
                HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, 
                new MockSecurityContext(principal));


        super.mockMvc
            .perform(
                    get("/api/v1/resource/test")
                    .session(session))
            .andExpect(status().isOk());
    }
}

2
เรายังไม่ได้เพิ่มการสนับสนุนอย่างเป็นทางการสำหรับ Spring Security ดูjira.springsource.org/browse/SEC-2015โครงร่างของสิ่งที่จะมีลักษณะระบุไว้ในgithub.com/SpringSource/spring-test-mvc/blob/master/src/test/…
Rob Winch

ฉันไม่คิดว่าการสร้างออบเจ็กต์การรับรองความถูกต้องและเพิ่มเซสชันด้วยแอตทริบิวต์ที่เกี่ยวข้องนั้นไม่ดี คุณคิดว่านี่เป็น "วิธีแก้ปัญหา" ที่ถูกต้องหรือไม่? ในทางกลับกันการสนับสนุนโดยตรงจะดีมากแน่นอน ดูเรียบร้อยสวย ขอบคุณสำหรับลิงค์!
Martin Becker

ทางออกที่ดี ทำงานให้ฉัน! เป็นเพียงปัญหาเล็กน้อยเกี่ยวกับการตั้งชื่อวิธีการป้องกันgetPrincipal()ซึ่งสำหรับความเห็นของฉันนั้นทำให้เข้าใจผิดเล็กน้อย getAuthentication()นึกคิดมันควรจะได้รับการตั้งชื่อ ในsignedIn()การทดสอบของคุณควรตั้งชื่อตัวแปรท้องถิ่นauthหรือauthenticationแทนที่จะเป็นprincipal
Tanvir

"getPrincipal (" test1 ") ¿คืออะไร ?? คุณช่วยอธิบายได้ไหมว่ามันคืออะไรขอบคุณล่วงหน้า
user2992476

@ user2992476 อาจส่งคืนวัตถุประเภท UsernamePasswordAuthenticationToken หรือคุณสร้าง GrantedAuthority และสร้างวัตถุนี้
bluelurker

31

เพิ่มใน pom.xml:

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <version>4.0.0.RC2</version>
    </dependency>

และใช้org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessorsสำหรับการขออนุญาต ดูตัวอย่างการใช้งานได้ที่https://github.com/rwinch/spring-security-test-blog ( https://jira.spring.io/browse/SEC-2592 )

ปรับปรุง:

4.0.0.RC2 ใช้งานได้กับสปริงความปลอดภัย 3.x สำหรับ spring-security 4 spring-security-test เป็นส่วนหนึ่งของ spring-security ( http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#testเวอร์ชันจะเหมือนกัน )

การตั้งค่ามีการเปลี่ยนแปลง: http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test-mockmvc

public void setup() {
    mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())  
            .build();
}

ตัวอย่างสำหรับพื้นฐานการตรวจสอบ: http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#testing-http-basic-authentication


นอกจากนี้ยังแก้ไขปัญหาของฉันในการรับ 404 เมื่อพยายามเข้าสู่ระบบผ่านตัวกรองความปลอดภัยในการเข้าสู่ระบบ ขอบคุณ!
Ian Newland

สวัสดีขณะทดสอบตามที่ GKislin กล่าวไว้ ฉันได้รับข้อผิดพลาดดังต่อไปนี้ "การตรวจสอบสิทธิ์ล้มเหลว UserDetailsService ส่งคืนค่า null ซึ่งเป็นการละเมิดสัญญาอินเทอร์เฟซ" ข้อเสนอแนะใด ๆ โปรด AuthenticationRequest ขั้นสุดท้าย = AuthenticationRequest ใหม่ (); auth.setUsername (หมายเลขผู้ใช้); auth.setPassword (รหัสผ่าน); mockMvc.perform (.. โพสต์ ( "/ API / รับรองความถูกต้อง /") เนื้อหา (JSON (รับรองความถูกต้อง)) contentType (MediaType.APPLICATION_JSON));
Sanjeev

7

นี่คือตัวอย่างสำหรับผู้ที่ต้องการทดสอบ Spring MockMvc Security Config โดยใช้การพิสูจน์ตัวตนพื้นฐาน Base64

String basicDigestHeaderValue = "Basic " + new String(Base64.encodeBase64(("<username>:<password>").getBytes()));
this.mockMvc.perform(get("</get/url>").header("Authorization", basicDigestHeaderValue).accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());

การพึ่งพา Maven

    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.3</version>
    </dependency>

3

คำตอบสั้น ๆ :

@Autowired
private WebApplicationContext webApplicationContext;

@Autowired
private Filter springSecurityFilterChain;

@Before
public void setUp() throws Exception {
    final MockHttpServletRequestBuilder defaultRequestBuilder = get("/dummy-path");
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
            .defaultRequest(defaultRequestBuilder)
            .alwaysDo(result -> setSessionBackOnRequestBuilder(defaultRequestBuilder, result.getRequest()))
            .apply(springSecurity(springSecurityFilterChain))
            .build();
}

private MockHttpServletRequest setSessionBackOnRequestBuilder(final MockHttpServletRequestBuilder requestBuilder,
                                                             final MockHttpServletRequest request) {
    requestBuilder.session((MockHttpSession) request.getSession());
    return request;
}

หลังจากดำเนินการformLoginจากการทดสอบความปลอดภัยในฤดูใบไม้ผลิแต่ละคำขอของคุณจะถูกเรียกโดยอัตโนมัติว่าเป็นผู้ใช้ที่เข้าสู่ระบบ

คำตอบยาว:

ตรวจสอบโซลูชันนี้ (คำตอบสำหรับสปริง 4): วิธีล็อกอินผู้ใช้ด้วยการทดสอบสปริง 3.2 mvc ใหม่


2

ตัวเลือกเพื่อหลีกเลี่ยงการใช้ SecurityContextHolder ในการทดสอบ:

  • ตัวเลือกที่ 1 : ใช้ล้อเลียน - ฉันหมายถึงล้อเลียนSecurityContextHolderโดยใช้ห้องสมุดจำลอง - EasyMockตัวอย่าง
  • ตัวเลือกที่ 2 : โทรห่อSecurityContextHolder.get...ในรหัสของคุณในการให้บริการบาง - ตัวอย่างเช่นในSecurityServiceImplด้วยวิธีการgetCurrentPrincipalที่ดำเนินการติดต่อและจากนั้นในการทดสอบของคุณคุณก็สามารถสร้างการดำเนินการจำลองของอินเตอร์เฟซนี้ว่าผลตอบแทนที่ต้องการหลักไม่สามารถเข้าถึงSecurityServiceSecurityContextHolder

อืมอาจจะไม่ได้ภาพรวม ปัญหาของฉันคือ SecurityContextPersistenceFilter แทนที่ SecurityContext โดยใช้ SecurityContext จาก HttpSessionSecurityContextRepository ซึ่งจะอ่าน SecurityContext จาก HttpSession ที่เกี่ยวข้อง ดังนั้นวิธีแก้ปัญหาโดยใช้เซสชัน เกี่ยวกับการโทรไปยัง SecurityContextHolder: ฉันแก้ไขคำตอบของฉันเพื่อที่ฉันจะไม่ใช้การโทรไปที่ SecurityContextHolder อีกต่อไป แต่ยังไม่มีการแนะนำการห่อหรือการเยาะเย้ยห้องสมุดเพิ่มเติม คุณคิดว่านี่เป็นทางออกที่ดีกว่าหรือไม่?
Martin Becker

ขออภัยฉันไม่เข้าใจสิ่งที่คุณกำลังมองหาและฉันไม่สามารถให้คำตอบที่ดีไปกว่าวิธีแก้ปัญหาที่คุณคิดขึ้นมาได้และดูเหมือนว่าจะเป็นตัวเลือกที่ดี
Pavla Nováková

เอาล่ะขอบคุณ ฉันจะยอมรับข้อเสนอของฉันเพื่อเป็นทางออกในตอนนี้
Martin Becker
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.