英文:
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'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("/v1/my-controller/type", 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("")));
}
Because it requires resource server. But the problem is that when I'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 'org.springframework.security.oauth2.provider.token.TokenStore' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.provider.token.TokenStore' 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("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;
}
}
}
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("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);
}
}
Then to disable security in controller test you need to do next
@WebMvcTest(MyController.class)
@AutoConfigureMockMvc(addFilters = false)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论