使用 JUnit 测试具有 @PreAuthorize 的服务方法

Posted

技术标签:

【中文标题】使用 JUnit 测试具有 @PreAuthorize 的服务方法【英文标题】:Test service method that has @PreAuthorize with JUnit 【发布时间】:2019-06-22 19:22:37 【问题描述】:

在 Spring Boot 1.4.1 中,我想对使用 @PreAuthorize 访问受限的服务方法进行单元测试(而不是集成测试)。

问题是从 Eclipse 启动单独测试时似乎没有检查 @PreAuthorize(尚未尝试使用 mvn test)。

然后,如果我奇迹般地设法正确运行测试,我将尝试使用 StudyService 接口,而不是 impl。

这是我的测试课:

@RunWith(MockitoJUnitRunner.class)
public class TestMethodSecurity 

    private static final Long STUDY_ID = 1L;

    @Mock
    private StudyRepository studyRepository;

    @InjectMocks
    private StudyServiceImpl studyService;


    @Before
    public void setup() 
        given(studyRepository.findOne(STUDY_ID)).willReturn(ModelsUtil.createStudy());
    


    @Test
    public void findByIdWithoutAccessRightTest() 
        Study study = studyService.findById(STUDY_ID);
    

这是服务:

@Service
public class StudyServiceImpl implements StudyService 


    @Autowired
    private StudyRepository studyRepository;


    @Override
    @PreAuthorize("hasAnyRole('DOES NOT EVENT CHECK SYNTAX")
    public Study findById(final Long id) 
        return studyRepository.findOne(id);
    


测试成功,就是失败。

【问题讨论】:

您可能想查看@WithUserDetails 您找到解决方案了吗?我有同样的问题 - @PreAuthorize 没有在测试中检查,端点总是返回 http 200,而它们应该返回 http 403。 @user2551317 是的,是@EnableGlobalMethodSecurity(prePostEnabled = true),如果我记得的话,我会在星期一写一个完整的解决方案。 @Julien 如果你能写出这个问题的完整解决方案,我将不胜感激,当我添加这个注释时,我得到了 BeanDefinitionOverrideException: Invalid bean definition with name 'metaDataSourceAdvisor'。也许这与其他注释混合在一起。 @user2551317 我在下面写了我的解决方案。关于你的错误,我不记得有那个,我检查***.com/questions/53834643/… 【参考方案1】:

如果你想测试 Spring Security 注解,你需要使用SpringRunner,它将创建 Spring Context 并将 Spring Security 代码注入代理类。

如何测试Spring Security你可以找到here。

【讨论】:

谢谢。所以我添加了@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration 但它不会改变结果(测试成功)【参考方案2】:

注释你的测试类
@RunWith(SpringRunner.class)
@ContextConfiguration

你的方法应该是这样的

@Test(expected = AccessDeniedException.class)
@WithAnonymousUser
@Test
    public void findByIdWithoutAccessRightTest() 
        Study study = studyService.findById(STUDY_ID);
    

如果不加载至少安全上下文,您将无法测试安全性。

你可能想看看这个例子:

public ApplicationUser getApplicationUser() 
    ApplicationUser applicationUser = (ApplicationUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    return applicationUser;


import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
import org.mockito.MockitoAnnotations;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import com.arpit.security.user.ApplicationUser;

public class BaseTest 

    @Before
    public void setupMock() 
        MockitoAnnotations.initMocks(this);
    

    @Test
    public void mockApplicationUser() 
        ApplicationUser applicationUser = mock(ApplicationUser.class);
        Authentication authentication = mock(Authentication.class);
        SecurityContext securityContext = mock(SecurityContext.class);
        when(securityContext.getAuthentication()).thenReturn(authentication);
        SecurityContextHolder.setContext(securityContext);
        when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(applicationUser);
    


取自:https://www.javacodegeeks.com/2017/05/mocking-spring-security-context-unit-testing.html

我想您的服务方法故意存在语法错误? (缺少括号和错误的引号?)

【讨论】:

在@PreAuthorize 上,是的。谢谢,我会尽快检查。【参考方案3】:

这就是我的工作方式:

@RunWith(SpringRunner.class)
@SpringBootTest
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ActiveProfiles("test")
public class StudySecurityTest 

    @Autowired
    private StudyService service;

    @MockBean
    private StudyRepository repository;

    @Test
    @WithAnonymousUser
    public void testAsAnonymous() 
        ...
    

    @Test
    @WithMockKeycloakUser(id = LOGGED_USER_ID, username = LOGGED_USER_USERNAME, authorities =  "ROLE_USER" )
    public void testAsUser() 
        ...
    

如果你对@WithMockKeycloakUser感兴趣,可以问我。

【讨论】:

以上是关于使用 JUnit 测试具有 @PreAuthorize 的服务方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Junit 5 编写具有 System.exit() 的测试用例方法?

Junit测试具有数据库访问权限的多线程应用程序[重复]

JUnit是否支持测试的属性文件?

具有依赖项的 Maven2 + JMeter + JUnit

同一对象的 JUnit 测试

如何对具有 Mono 返回类型的方法进行 junit 测试