测试使用Spring Security OAuth的Spring REST控制器

huangapple go评论125阅读模式
英文:

Testing Spring rest controller with Spring Security OAuth

问题

以下是您要翻译的内容:

我正在尝试使用Spring Boot的@WebMvcTest测试我的控制器我还实现了Spring OAuth安全性资源和授权服务器),因此要访问端点您需要经过授权

因此我不能像这样测试我的控制器

@WebMvcTest(MyController.class)
public class MyControllerTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private MyService service;

    @Test
    public void myTest() throws Exception {
        // 准备请求

        // 执行
        mvc.perform
            (MockMvcRequestBuilders
                .post("/v1/my-controller/type", type.name())
                .characterEncoding(StandardCharsets.UTF_8.name())
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(requestDTO)))
            // 断言
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(content().json(objectMapper.writeValueAsString("")));
    }
}

由于它需要资源服务器,所以我无法这样测试我的控制器。但问题是,当我运行这个测试时,我得到了以下错误:

***************************
应用启动失败
***************************

描述:

com.danfoss.cloud.prosa.restapi.security.WebSecurityConfig$OAuthResourceServer 中的构造函数的参数0需要一个类型为 'org.springframework.security.oauth2.provider.token.TokenStore' 的 bean,但找不到该类型的 bean。

操作:

在您的配置中考虑定义一个类型为 'org.springframework.security.oauth2.provider.token.TokenStore' 的 bean。

我的资源服务器看起来像这样:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Configuration
    @EnableResourceServer
    public static class OAuthResourceServer extends ResourceServerConfigurerAdapter {

        private final TokenStore tokenStore;
        //// 在 Web 上下文启动期间无法注入
        @Autowired
        public OAuthResourceServer(TokenStore tokenStore) {
            this.tokenStore = tokenStore;
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources
                .resourceId("resource")
                .tokenStore(tokenStore);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                .antMatchers("/status")
                .permitAll()
                .anyRequest()
                .access("isAuthenticated()");
        }

        private String createAuthorizationExpression(String ipWhitelist) {
            return null;
        }
    }
}

一个重要的事情是应用程序可以正常运行,并且在运行时可以成功注入TokenStore。

如何解决这个问题?


<details>
<summary>英文:</summary>

I&#39;m trying to test my controller with Spring boot @WebMvcTest. I also have implemented SPring OAuth security (resource and authorization server), so to reach endpoint you need to be authorized.

For this reason I cannot test my controller like this:

    @WebMvcTest(MyController.class)
    public class MyControllerTest {
    
        @Autowired
        private MockMvc mvc;
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @MockBean
        private MyService service;
    
        @Test
        public void myTest() throws Exception {
            //Arrange request
            
            //Act
            mvc.perform
                (MockMvcRequestBuilders
                    .post(&quot;/v1/my-controller/type&quot;, type.name())
                    .characterEncoding(StandardCharsets.UTF_8.name())
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(requestDTO)))
                //Assert
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(content().json(objectMapper.writeValueAsString(&quot;&quot;)));
    
        }

Because it requires resource server. But the problem is that when I&#39;m running this test I got next error:

        ***************************
    APPLICATION FAILED TO START
    ***************************
    
    Description:
    
    Parameter 0 of constructor in com.danfoss.cloud.prosa.restapi.security.WebSecurityConfig$OAuthResourceServer required a bean of type &#39;org.springframework.security.oauth2.provider.token.TokenStore&#39; that could not be found.
    
    
    Action:
    
    Consider defining a bean of type &#39;org.springframework.security.oauth2.provider.token.TokenStore&#39; in your configuration.

My Resource server looks like 

    
```java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Configuration
    @EnableResourceServer
    public static class OAuthResourceServer extends ResourceServerConfigurerAdapter {

         
        private final TokenStore tokenStore;
        ////CANNOT INJECT DURING WEB CONTEXT START
        @Autowired
        public OAuthResourceServer(TokenStore tokenStore) {
            this.tokenStore = tokenStore;
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources
                    .resourceId(&quot;resource&quot;)
                    .tokenStore(tokenStore);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                .antMatchers(&quot;/status&quot;)
                .permitAll()
                .anyRequest()
                .access(&quot;isAuthenticated()&quot;);
        }

        private String createAuthorizationExpression(String ipWhitelist) {
            return null;
        }
    }
}

One important thing that application works fine and it injects TokenStore fine during run.

How to overcome this issue?

答案1

得分: 1

通过这样解耦配置来解决此问题:

@Configuration
public class Config {

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(mainDataSource());
    }

    ....
    其他 beans
}

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class OAuthResourceServer extends ResourceServerConfigurerAdapter {

    private final TokenStore tokenStore;

    @Autowired
    public OAuthResourceServer(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
            .resourceId("resource")
            .tokenStore(tokenStore);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers(
                "/status",
                "/")
            .permitAll()
            .anyRequest()
            .access(
                            ...
                        );
    }
}

@Configuration
@EnableAuthorizationServer
public class OAuthAuthorizationServer extends AuthorizationServerConfigurerAdapter {

        ...
    private final AuthenticationManager authenticationManager;
      ...
    private final TokenStore tokenStore;
     ...

    @Autowired
    public OAuthAuthorizationServer(
    ....
        AuthenticationManager authenticationManager,
        TokenStore tokenStore,
    ...
    ) {
        this.restUserDetailsService = restUserDetailsService;
        this.oAuthRestAuthenticationProvider = oAuthRestAuthenticationProvider;
        this.authenticationManager = authenticationManager;
        this.jdbcClientDetailsService = jdbcClientDetailsService;
        this.tokenStore = tokenStore;
        this.authorizationCodeServices = authorizationCodeServices;
    }

    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
        tokenServices.setRefreshTokenValiditySeconds(refreshTokenValiditySeconds);
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setReuseRefreshToken(true);
        tokenServices.setTokenStore(tokenStore);
        tokenServices.setClientDetailsService(jdbcClientDetailsService);
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setAuthenticationManager(authenticationManager);
        return tokenServices;
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(oAuthRestAuthenticationProvider);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .approvalStoreDisabled()
            .authorizationCodeServices(authorizationCodeServices)
            .tokenStore(tokenStore)
            .tokenServices(tokenServices())
            .authenticationManager(authenticationManager)
            .userDetailsService(restUserDetailsService);
    }
}

然后在控制器测试中禁用安全性您需要执行以下操作

```java
@WebMvcTest(MyController.class)
@AutoConfigureMockMvc(addFilters = false)
英文:

Resolved this issue by decoupling configurations like this:

@Configuration
public class Config {
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(mainDataSource());
}
....
other beans
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
@Configuration
@EnableWebSecurity
@EnableResourceServer
public class OAuthResourceServer extends ResourceServerConfigurerAdapter {
private final TokenStore tokenStore;
@Autowired
public OAuthResourceServer(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId(&quot;resource&quot;)
.tokenStore(tokenStore);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(
&quot;/status&quot;,
&quot;/&quot;)
.permitAll()
.anyRequest()
.access(
...
);
}
}
@Configuration
@EnableAuthorizationServer
public class OAuthAuthorizationServer extends AuthorizationServerConfigurerAdapter {
...
private final AuthenticationManager authenticationManager;
...
private final TokenStore tokenStore;
...
@Autowired
public OAuthAuthorizationServer(
....
AuthenticationManager authenticationManager,
TokenStore tokenStore,
...
) {
this.restUserDetailsService = restUserDetailsService;
this.oAuthRestAuthenticationProvider = oAuthRestAuthenticationProvider;
this.authenticationManager = authenticationManager;
this.jdbcClientDetailsService = jdbcClientDetailsService;
this.tokenStore = tokenStore;
this.authorizationCodeServices = authorizationCodeServices;
}
@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setAccessTokenValiditySeconds(accessTokenValiditySeconds);
tokenServices.setRefreshTokenValiditySeconds(refreshTokenValiditySeconds);
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(jdbcClientDetailsService);
tokenServices.setSupportRefreshToken(true);
tokenServices.setAuthenticationManager(authenticationManager);
return tokenServices;
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(oAuthRestAuthenticationProvider);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(jdbcClientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.approvalStoreDisabled()
.authorizationCodeServices(authorizationCodeServices)
.tokenStore(tokenStore)
.tokenServices(tokenServices())
.authenticationManager(authenticationManager)
.userDetailsService(restUserDetailsService);
}
}

Then to disable security in controller test you need to do next

@WebMvcTest(MyController.class)
@AutoConfigureMockMvc(addFilters = false)

huangapple
  • 本文由 发表于 2020年8月18日 00:41:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63455088.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定