如何为Spring Security的SecurityConfig编写单元测试

问题描述 投票:0回答:2

我有一个 Spring Security 类,用于验证用户的令牌。我从 Auth0 网站获取了代码,并修改了

antMatcher
部分以适应我的配置。这是代码:

@EnableWebSecurity
public class SecurityConfig {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuer;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        /*
        This is where we configure the security required for our endpoints and setup our app to serve as
        an OAuth2 Resource Server, using JWT validation.
        */
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.GET, "/data/actuator/**").permitAll()
            .antMatchers(HttpMethod.PUT, "/data/**").hasAuthority("SCOPE_data:write")
            .anyRequest().authenticated()
            .and().cors()
            .and().oauth2ResourceServer().jwt();
        return http.build();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        /*
        By default, Spring Security does not validate the "aud" claim of the token, to ensure that this token is
        indeed intended for our app. Adding our own validator is easy to do:
        */
        NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
                JwtDecoders.fromOidcIssuerLocation(issuer);
        OAuth2TokenValidator<Jwt> audienceValidator =
                new com.nuance.pindata.health.importer.security.AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
        jwtDecoder.setJwtValidator(withAudience);
        return jwtDecoder;
    }
}

我现在正在尝试编写单元测试,但是没有好的方法来测试它。我实际上可以测试更改方法/路径,但如何编写此单元测试并不简单,它可以通过集成(自动化)测试来完成。

Spring Security HttpSecurity 配置测试,他建议不要为此类安全配置编写单元测试。这里正确的方法是什么?如果我应该编写单元测试,我怎样才能实现这一目标?

java spring-boot unit-testing spring-security auth0
2个回答
7
投票

您只能在集成测试中测试执行器端点访问控制 (

@SpringBootTest
)。对于您自己的安全
@Components
,您也可以在单元测试中执行此操作(此存储库中的许多示例):

  • @Controller
    @WebMvcTest
    (如果您使用的是反应式应用程序,则为
    @WebfluxTest
  • 带有被测试组件的
    @ExtendWith(SpringExtension.class)
    @EnableMethodSecurity
    @Import
    的普通 JUnit(
    @Service
    @Repository
    ,具有像
    @PreAuthorize
    表达式这样的方法安全性),以获得具有安全性的自动装配实例

spring-security-test
附带了一些 MockMvc 请求后处理器(在您的情况下请参阅
org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt
)以及 WebTestClient 变异器(请参阅
org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockJwt
)来配置正确类型的身份验证(在您的情况下为
JwtAuthenticationToken
)并设置它在测试安全上下文中,但这仅限于 MockMvc 和 WebTestClient 以及
@Controller
测试。

执行器启动的集成测试(

@SpringBootTest
)中的示例用法(但您了解单元测试的想法):

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;

@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
class ApplicationIntegrationTest {

    @Autowired
    MockMvc api;

    @Test
    void givenUserIsAnonymous_whenGetLiveness_thenOk() throws Exception {
        api.perform(get("/data/actuator/health/liveness"))
            .andExpect(status().isOk());
    }

    @Test
    void givenUserIsAnonymous_whenGetMachin_thenUnauthorized() throws Exception {
        api.perform(get("/data/machin"))
            .andExpect(status().isUnauthorized());
    }

    @Test
    void givenUserIsGrantedWithDataWrite_whenGetMachin_thenOk() throws Exception {
        api.perform(get("/data/machin")
                .with(jwt().jwt(jwt -> jwt.authorities(List.of(new SimpleGrantedAuthority("SCOPE_data:write"))))))
            .andExpect(status().isOk());
    }

    @Test
    void givenUserIsAuthenticatedButNotGrantedWithDataWrite_whenGetMachin_thenForbidden() throws Exception {
        api.perform(get("/data/machin")
                .with(jwt().jwt(jwt -> jwt.authorities(List.of(new SimpleGrantedAuthority("SCOPE_openid"))))))
            .andExpect(status().isForbidden());
    }
}

您也可以使用

@WithMockJwtAuth
中的我维护的库。该存储库包含相当多的用于任何类型
@Component
的单元和集成测试的示例(当然还有
@Controllers
或用方法安全性装饰的
@Services
)。
以上示例变为:

@Repositories

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-oauth2-test</artifactId>
    <version>6.0.12</version>
    <scope>test</scope>
</dependency>
Spring 插件启动器

在与测试注释相同的存储库中,您将找到简化资源服务器安全配置的入门工具(还可以改进 CORS 配置并同步会话和 CSRF 保护禁用,因为

第二个不应在活动会话中禁用

... ). 使用非常简单,

切换到另一个 OIDC 授权服务器所需更改的只是属性

。例如,这种情况可能会发生,因为您被迫忙碌(如果他们认为 Auth0 太昂贵或不再可信),或者可能是因为您发现在开发计算机上使用独立的 Keycloak 更方便(它是可以离线使用,我经常这样做)。 不要直接导入

@SpringBootTest(webEnvironment = WebEnvironment.MOCK) @AutoConfigureMockMvc class ApplicationIntegrationTest { @Autowired MockMvc api; @Test void givenUserIsAnonymous_whenGetLiveness_thenOk() throws Exception { api.perform(get("/data/actuator/health/liveness")) .andExpect(status().isOk()); } @Test void givenUserIsAnonymous_whenGetMachin_thenUnauthorized() throws Exception { api.perform(get("/data/machin")) .andExpect(status().isUnauthorized()); } @Test @WithMockJwtAuth("SCOPE_data:write") void givenUserIsGrantedWithDataWrite_whenGetMachin_thenOk() throws Exception { api.perform(get("/data/machin")) .andExpect(status().isOk()); } @Test @WithMockJwtAuth("SCOPE_openid") void givenUserIsAuthenticatedButNotGrantedWithDataWrite_whenGetMachin_thenForbidden() throws Exception { api.perform(get("/data/machin")) .andExpect(status().isForbidden()); } }

,而是导入一个薄包装器(

仅由 3 个文件
组成): spring-boot-starter-oauth2-resource-server

默认情况下,用户必须经过身份验证才能访问除 
<dependency> <groupId>com.c4-soft.springaddons</groupId> <artifactId>spring-addons-webmvc-jwt-resource-server</artifactId> <version>6.0.12</version> </dependency>

属性中列出的路由之外的任何路由(见下文)。将所有 Java 配置替换为:

com.c4-soft.springaddons.security.permit-all

您可以删除所有 
@Configuration @EnableMethodSecurity public class SecurityConfig { // If not using method-security or to configure actuator RBAC // You might define a bean of type ExpressionInterceptUrlRegistryPostProcessor // and Fine tune AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry }

属性,它会被忽略(如果您验证受众,则

spring.security.oauth2.resourceserver
除外)。可以使用的属性有:
spring.security.oauth2.resourceserver.jwt.audiences

傻瓜,不是吗?


0
投票

希望对你有用。

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.