コントローラーの統合テストを実行しようとしていますが、認証しないと問題が発生します。これが私のコントローラーです。
_@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"security.basic.enabled=false", "management.security.enabled=false"})
@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class})
public class HelloControllerIT {
private final ObjectMapper mapper = new ObjectMapper();
@Autowired private TestRestTemplate template;
@Test
public void test1() throws Exception {
ObjectNode loginRequest = mapper.createObjectNode();
loginRequest.put("username","name");
loginRequest.put("password","password");
JsonNode loginResponse = template.postForObject("/authenticate", loginRequest.toString(), JsonNode.class);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());
headers.add("Content-Type", "application/json");
return new HttpEntity<>(null, headers);
HttpEntity request = getRequestEntity();
ResponseEntity response = template.exchange("/get",
HttpMethod.GET,
request,
new ParameterizedTypeReference<List<Foo>>() {});
//assert stuff
}
}
_
これを実行すると、すべてが機能します。しかし、行をコメントアウトすると:
_headers.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());
_
エラーが表示されます:
_org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON document: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
at [Source: Java.io.PushbackInputStream@272a5bc6; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
at [Source: Java.io.PushbackInputStream@272a5bc6; line: 1, column: 1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.Java:234)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.Java:219)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.Java:95)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.Java:917)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.Java:901)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.Java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.Java:613)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.Java:559)
at org.springframework.boot.test.web.client.TestRestTemplate.exchange(TestRestTemplate.Java:812)
at com.test.HelloControllerIT.test1(HelloControllerIT.Java:75)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.lang.reflect.Method.invoke(Method.Java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.Java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.Java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.Java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.Java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.Java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.Java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.Java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.Java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.Java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.Java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.Java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.Java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.Java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.Java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.Java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.Java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:70)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
at [Source: Java.io.PushbackInputStream@272a5bc6; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.Java:270)
at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.Java:1234)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.Java:1122)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.Java:1075)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.Java:338)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:269)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:259)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:26)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.Java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.Java:2922)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.Java:231)
... 38 more
_
明らかに、上部のセキュリティアノテーションは機能していません。それでは、問題は正確に何であり、どうすれば修正できますか?
Edit 1:私はやってみました:
_Object response = template.exchange("/get", HttpMethod.GET, request, Object.class);
_
そして得た:
_<401 Unauthorized,{status=401, message=Authentication failed, errorCode=10, timestamp=1497654855545},{X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY], Content-Type=[application/json;charset=ISO-8859-1], Content-Length=[89], Date=[Fri, 16 Jun 2017 23:14:15 GMT]}>
_
セキュリティのために _org.springframework.security.authentication.AuthenticationProvider
_ と _org.springframework.security.authentication.AuthenticationManager
_ を使用しています
編集2:スカディアの提案に従って、私は次のような新しいクラスを作成しました:
_@Configuration
public class AnonymousConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity web) throws Exception {
web.antMatcher("**/*").anonymous();
}
}
_
しかし、統合テストを実行すると、次のエラーが表示されます。
_Java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.Java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.Java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.Java:47)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.Java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.Java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.Java:287)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.Java:289)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:247)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.Java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.Java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.Java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.Java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.Java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.Java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.Java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.Java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:70)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Injection of autowired dependencies failed; nested exception is Java.lang.IllegalStateException: @Order on WebSecurityConfigurers must be unique. Order of 100 was already used on config.AnonymousConfig$$EnhancerBySpringCGLIB$$ba18b8d7@6291f725, so it cannot be used on security.WebSecurityConfig$$EnhancerBySpringCGLIB$$9d88e7e@1bfaaae1 too.
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.Java:372)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.Java:1264)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.Java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.Java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.Java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.Java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.Java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.Java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.Java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.Java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.Java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.Java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.Java:737)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.Java:370)
at org.springframework.boot.SpringApplication.run(SpringApplication.Java:314)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.Java:120)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.Java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.Java:116)
... 23 more
Caused by: Java.lang.IllegalStateException: @Order on WebSecurityConfigurers must be unique. Order of 100 was already used on config.AnonymousConfig$$EnhancerBySpringCGLIB$$ba18b8d7@6291f725, so it cannot be used on security.WebSecurityConfig$$EnhancerBySpringCGLIB$$9d88e7e@1bfaaae1 too.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(WebSecurityConfiguration.Java:148)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
at Java.lang.reflect.Method.invoke(Method.Java:498)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.Java:701)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.Java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.Java:366)
... 40 more
_
通常のプロジェクトにあるwebsecurityの設定と衝突しているようです。そのファイルは次のとおりです。
_@EnableWebSecurity
@EnableWebMvc
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//configuration
}
_
上記の問題を修正した@Order(1000)
を追加しようとしましたが、それでも_401 Unauthorized
_になりました
さらにいくつかの自動構成を除外してみることができます。
_@EnableAutoConfiguration(exclude = {
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration.class
})
_
ところで、ものを除外するよりエレガントな方法は、テストソースで_application-test.properties
_を定義し、@Profile("test")
でテストをマークすることです。次に、これを設定に追加します。
_spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration
_
除外可能なすべての構成は、ここで見つけることができます: spring.factories
スプリングブート統合テストで認証を提供するオプションがいくつかあります。すべてを最後まで機能させるには、いくつかの調整が必要になる場合があります。
モックベースのアプローチ
これは、 MockMvc@ WithMockUser アノテーションに挿入されたテストWebApplicationContext
を使用して認証ユーザーを提供し、WithMockUserSecurityContextFactory
が模擬ユーザーのセキュリティコンテキストを作成します。
SecurityMockMvcConfigurers
は、セキュリティフィルターspringSecurityFilterChain
をMockMvc
に登録します。
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class HelloControllerIT {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity()) // enable security for the mock set up
.build();
}
@WithMockUser(value = "test", password = "pass")
@Test
public void test() throws Exception {
String contentType = MediaType.APPLICATION_JSON + ";charset=UTF-8";
String authzToken = mvc
.perform(
post("/authenticate")
.contentType(
MediaType.APPLICATION_JSON).
content("")).
andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.token", is(notNullValue())))
.andReturn().getResponse().getContentAsString();
System.out.print(authzToken);//{"token":"1a3434a"}
}
}
メモリ内認証プロバイダーベースのアプローチ
これは、メモリ内認証プロバイダーと基本認証ユーザーを使用します。
インメモリ認証プロバイダーを登録し、基本認証を有効にし、HttpSecurity
のWebSecurityConfigurerAdapter
で匿名アクセスを無効にします。
メモリ内プロバイダーが登録されると、DefaultInMemoryUserDetailsManagerConfigurer
はメモリ内に基本認証ユーザーを作成します。
基本認証が有効な場合、HttpBasicConfigurer
はBasicAuthenticationFilter
を構成します。テストユーザーを認証し、セキュリティコンテキストを作成します。
セキュリティ構成
@EnableWebSecurity
@EnableWebMvc
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// register test user with in memory authentication provider
auth.inMemoryAuthentication().withUser("test").password("pass").roles("ROLES");
}
@Override
public void configure(HttpSecurity http) throws Exception {
// enable basic authentication & disable anoymous access
http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().anonymous().disable();
}
}
認証エンドポイント
@Controller
@RequestMapping("/authenticate")
public class AuthenticationController {
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public TokenClass getToken() {
TokenClass tokenClass = new TokenClass();
tokenClass.setToken("1a3434a");
return tokenClass;
}
}
ポジョ
public class TokenClass {
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
テストコントローラー
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
import org.springframework.test.context.junit4.SpringRunner;
import Java.util.Arrays;
import Java.util.Base64;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT {
@Autowired
private TestRestTemplate template;
@Test
public void test() throws Exception {
HttpHeaders authHeaders = new HttpHeaders();
String token = new String(Base64.getEncoder().encode(
("test" + ":" + "pass").getBytes()));
authHeaders.set("Authorization", "Basic " + token);
JsonNode loginResponse = template.postForObject("/authenticate", new HttpEntity<>(null, authHeaders), JsonNode.class);
HttpHeaders authzHeaders = new HttpHeaders();
authzHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
authzHeaders.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());
authzHeaders.add("Content-Type", "application/json");
ResponseEntity response = template.exchange("/secure",
HttpMethod.GET,
new HttpEntity<>(null, authzHeaders),
String.class
);
}
}
デフォルトのセキュリティ設定が導入されているようです。完全な構成が表示されない限り、これを確認するのは困難です。可能であれば、最小限のプロジェクトを(githubに)投稿してください。
統合テストの実行中に認証を強制したくないので、アプリケーションリソースの匿名アクセスを有効にすることができます。
匿名アクセスを有効にするには、テストソースディレクトリの下にクラスを追加します。 Webアプリケーションのブートストラップ中に匿名アクセスを構成します。 (401応答コードは表示されません)
@Configuration
public class AllowAnonymousWebAccess extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity web) throws Exception {
web.antMatcher("**/*").anonymous();
}
}
この問題に長い間直面していました。最後に解決しました。テストプロファイルを作成する承認サーバーをモックする必要があり、Spring Securityユーザー詳細サービスをモックする必要もあります。 blog で見つけたコードを次に示します。
テスト認証サーバー
@Configuration
@EnableAuthorizationServer
@ActiveProfiles("test")
public class AuthorizationTestServer extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager;
@Autowired
public AuthorizationTestServer(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("permitAll()");
oauthServer.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
clients.inMemory()
.withClient("user")
.secret("password")
.authorizedGrantTypes("password")
.scopes("openid");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
endpoints.authenticationManager(this.authenticationManager);
}
}
ユーザー詳細サービスのテスト
@Service
@ActiveProfiles("test")
public class UserDetailTestService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return new User("dummyUser","dummyPassword",true,true,
true,true, AuthorityUtils.createAuthorityList("USER"));
}
}
メインテストクラス
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@AutoConfigureMockMvc
public class JmStudentServiceApplicationTests {
@Autowired
private WebApplicationContext wac;
@Autowired
private MockMvc mockMvc;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private StudentRepository studentRepository;
@Test
public void test() throws Exception{
String accessToken = obtainAccessToken("dummyUser", "dummyPassword");
Student student = new Student();
student.setId("2222");
student.setName("test student");
studentRepository.createStudent(student);
assertTrue(studentRepository.getStudentById("2222").getName().equals("test student"));
MvcResult result = mockMvc.perform(get("/students/by-id/2222")
.header("Authorization", "Bearer " + accessToken)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
String str = result.getResponse().getContentAsString();
assertTrue(str.contains("\"id\":\"2222\""));
}
private String obtainAccessToken(String username, String password) throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "password");
params.add("username", username);
params.add("password", password);
params.add("scope", "openid");
String base64ClientCredentials = new String(Base64.encodeBase64("user:password".getBytes()));
ResultActions result
= mockMvc.perform(post("/oauth/token")
.params(params)
.header("Authorization","Basic " + base64ClientCredentials)
.accept("application/json;charset=UTF-8"))
.andExpect(status().isOk());
String resultString = result.andReturn().getResponse().getContentAsString();
JacksonJsonParser jsonParser = new JacksonJsonParser();
return jsonParser.parseMap(resultString).get("access_token").toString();
}
}
認証は機能しているように見えますが、間違った方法で応答を処理しています。
応答をList<Foo>
として解析しようとしているコードは次のとおりです。
ResponseEntity response = template.exchange("/get",
HttpMethod.GET,
request,
new ParameterizedTypeReference<List<Foo>>() {}
);
ただし、認証ヘッダーを提供していないため、サーバーはカスタムエラー(明らかにJsonオブジェクトにラップされています)で応答し、Jsonオブジェクト(開始するArrayList
を解析できないというテストでこの例外を受け取ります。 START_OBJECT
トークンのような{
)。
Could not read JSON document: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
応答をオブジェクトとして処理して、実際にそこにあるものを確認できるようにしてください。
template.exchange("/get", HttpMethod.GET, request, Object.class);
しかし、これは最終的な解決策としては機能しません。 Http応答コードに基づいて応答本文を処理する必要があります、200の場合はList<>
として解析、それ以外の場合はMap<>
またはサーバーが返す構造として解析します。
インメモリ認証やモックなしでこれを行う方法を見つけました。
public class TestConf extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.permitAll();
}
}
また、スプリングアクティブプロファイルを使用して、テストケースの実行時にのみ上記の構成を実行します。
Spring Boot documentation によると、クラスに_@SpringBootTest
_注釈を付け、構成の代替を指定していない場合、Springは_@SpringBootApplication
_クラスを検索して提供しますプライマリ設定として。 Springは、テストクラスのパッケージで検索を開始し、パッケージ階層を検索します。おそらく、それはあなたの主要な設定を見つけており、これがあなたの望まないセキュリティ設定を含めてそれをもたらすすべてです。
Spring Boot 2.0.3で検証された最も簡単な解決策は、@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
を@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
に変更することです。この変更を行うと、Springはテストクラスをプライマリ構成クラスとして登録するため、除外を承認します。または、個別の構成クラスを作成して、基本パッケージに存在し、すべての統合テストで検出されるすべての統合テストで共有します。