質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Spring Security

Spring Securityは、Springのサブプロジェクトの一つでWebアプリケーションに必要な機能を追加します。正規ユーザーであるかを確認するための「認証機能」と、ユーザーのアクセスを制御する「認可機能」を簡単に追加することが可能です。

JUnit

JUnitは、Javaで開発されたプログラムのユニットテストを行うためのアプリケーションフレームワークです。簡単にプログラムのユニットテストを自動化することができ、結果もわかりやすく表示されるため効率的に開発時間を短縮できます。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Q&A

解決済

1回答

6614閲覧

Spring Security test で@AuthenticationPrincipalを引数としても持つControllerのテストがうまくできない。

montai21

総合スコア17

Spring Security

Spring Securityは、Springのサブプロジェクトの一つでWebアプリケーションに必要な機能を追加します。正規ユーザーであるかを確認するための「認証機能」と、ユーザーのアクセスを制御する「認可機能」を簡単に追加することが可能です。

JUnit

JUnitは、Javaで開発されたプログラムのユニットテストを行うためのアプリケーションフレームワークです。簡単にプログラムのユニットテストを自動化することができ、結果もわかりやすく表示されるため効率的に開発時間を短縮できます。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

1グッド

1クリップ

投稿2020/09/14 03:28

#事象
spring security test で@AuthenticationPrincipalのアノテーションをつけた引数として持つControllerのテストコードを書いてるのですが、テストを行うと、以下のエラーメッセージが出力されます。AccountUserDetailsクラスのインスタンス化に失敗しているという内容のエラーが出ていると思われるのですが、MockMvcのwith()でuser()にユーザ情報の変数を渡すことで上述したテストができると考えていたのですが、違うのでしょうか。
対処方法を教えていただけないでしょうか。
情報に過不足があれば、ご指摘ください。

#コンソールログ

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.taskmanagement.domain.service.userdetails.AccountUserDetails]: Constructor threw exception; nested exception is java.lang.NullPointerException ・・・省略

#Controller

package com.example.taskmanagement.app.schedule; @Controller @RequestMapping("schedule") public class ScheduleController { ・・・省略 @GetMapping("list") public String list(@AuthenticationPrincipal AccountUserDetails userDetails, Model model) { ・・・省略 return "schedule/list"; } ・・・省略 }

#Test

@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration(locations = {"classpath*:META-INF/spring/applicationContext.xml", "classpath*:META-INF/spring/test-context.xml", "classpath*:META-INF/spring/spring-mvc-test.xml"}) @Transactional public class ScheduleControllerTest { @Inject ScheduleController target; private MockMvc mockMvc; private AccountUserDetails userDetails; private SecurityContext securityContext; @Before public void setUp() throws Exception{ mockMvc = MockMvcBuilders.standaloneSetup(target).alwaysDo(log()).build(); } @Test @WithUserDetails(value="user", userDetailsServiceBeanName="accountUserDetailsService") public void testList() throws Exception{ UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); userDetails = (AccountUserDetails)authentication.getPrincipal(); mockMvc.perform(get("/schedule/list").with(user(userDetails))) .andDo(print()) .andExpect(status().isOk()) .andReturn(); } }

#AccountUserDetails

package com.example.taskmanagement.domain.service.userdetails; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import com.example.taskmanagement.domain.model.Account; public class AccountUserDetails extends User { private static final long serialVersionUID = 1L; private final Account account; public AccountUserDetails(Account account) { super(account.getUsername(), account.getPassword(), AuthorityUtils .createAuthorityList("ROLE_USER")); this.account = account; } public Account getAccount() { return account; } }

#applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <import resource="classpath:/META-INF/spring/taskmanagement-domain.xml" /> <bean id="passwordEncoder" class="org.springframework.security.crypto.password.DelegatingPasswordEncoder"> <constructor-arg name="idForEncode" value="pbkdf2" /> <constructor-arg name="idToPasswordEncoder"> <map> <entry key="pbkdf2"> <bean class="org.springframework.security.crypto.password.Pbkdf2PasswordEncoder" /> </entry> <entry key="bcrypt"> <bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> </entry> <!-- When using SCryptPasswordEncoder, you need to add bcprov-jdk15on.jar to the dependency. <entry key="scrypt"> <bean class="org.springframework.security.crypto.scrypt.SCryptPasswordEncoder" /> </entry> --> </map> </constructor-arg> </bean> <context:property-placeholder location="classpath*:/META-INF/spring/*.properties" /> <bean id="beanMapper" class="com.github.dozermapper.spring.DozerBeanMapperFactoryBean"> <property name="mappingFiles" value="classpath*:/META-INF/dozer/**/*-mapping.xml" /> </bean> <!-- Message --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>i18n/application-messages</value> </list> </property> <property name="defaultEncoding" value="UTF-8"></property> </bean> <!-- Exception Code Resolver. --> <bean id="exceptionCodeResolver" class="org.terasoluna.gfw.common.exception.SimpleMappingExceptionCodeResolver"> <!-- Setting and Customization by project. --> <property name="exceptionMappings"> <map> <entry key="ResourceNotFoundException" value="e.xx.fw.5001" /> <entry key="InvalidTransactionTokenException" value="e.xx.fw.7001" /> <entry key="BusinessException" value="e.xx.fw.8001" /> <entry key=".DataAccessException" value="e.xx.fw.9002" /> </map> </property> <property name="defaultExceptionCode" value="e.xx.fw.9001" /> </bean> <!-- Exception Logger. --> <bean id="exceptionLogger" class="org.terasoluna.gfw.common.exception.ExceptionLogger"> <property name="exceptionCodeResolver" ref="exceptionCodeResolver" /> </bean> <!-- Filter. --> <bean id="exceptionLoggingFilter" class="org.terasoluna.gfw.web.exception.ExceptionLoggingFilter" > <property name="exceptionLogger" ref="exceptionLogger" /> </bean> </beans>

#test-context.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath*:/META-INF/spring/*.properties" /> <!-- (1) --> <bean id="exceptionLogger" class="org.terasoluna.gfw.common.exception.ExceptionLogger" /> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> <!-- (2) --> <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /> </beans>

#spring-mvc-test.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <context:component-scan base-package="com.example.taskmanagement" /> </beans>

##参考にしたサイト
https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security#user-content-populating-a-test-user-with-a-requestpostprocessor

A-pZ👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

#対応内容
spring security testの理解が不足しておりました。
org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurityがSpring Security と Spring MVC テストを統合するために必要なすべての初期セットアップを実行してくれるらしいです。また、spring mvc、securityの機能と合わしてテストを行いたい場合は、webAppContextSetupを利用する必要があるらしいです。standaloneSetupはそのクラス単体のみのテストしかできないらしく、フレームワークを併用したテストはできないらしいです。

以下のようにTestコードを修正しました。
webAppContextSetupでWebApplicationContextを生成するようにし、.apply(springSecurity()) でSpring Security と Spring MVC テストを統合するために必要なすべての初期セットアップを実行します。(WebApplicationContextがweb.xmlなどの定義情報を保持しています。@WebAppConfigurationでロードしています。spring securityを利用する場合は、web.xmlに定義する必要があります。)

Test

1@RunWith(SpringJUnit4ClassRunner.class) 2@WebAppConfiguration //コンテキスト使う準備 3@ContextConfiguration(locations = {"classpath*:META-INF/spring/applicationContext.xml", 4 "classpath*:META-INF/spring/spring-security.xml", 5 "classpath*:META-INF/spring/spring-mvc.xml"}) 6//どのBean定義ファイルを使うか指定 7@Transactional //テスト終了後にロールバックするために定義 8public class ScheduleControllerTest { 9 10 private MockMvc mockMvc; 11 12 @Autowired 13 private WebApplicationContext wac; //コンテキストを用意 14 15 private AccountUserDetails userDetails; 16 17 @Before 18 public void setUp() throws Exception{ 19 20 mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(springSecurity()).alwaysDo(log()).build(); 21 } 22 23 @Test 24 @WithUserDetails(value="user", userDetailsServiceBeanName="accountUserDetailsService") 25 public void testList() throws Exception{ 26 27 UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); 28 userDetails = (AccountUserDetails)authentication.getPrincipal(); 29 mockMvc.perform(get("/schedule/list").with(user(userDetails))) //performでGET通信の振る舞い 30 .andDo(print()) //コンソールにプリント 31 .andExpect(status().isOk()) //statusコードの確認 32 .andReturn(); //結果をmvcResultへ返却 33 } 34}

#参考サイト
https://spring.pleiades.io/spring-security/site/docs/5.0.19.BUILD-SNAPSHOT/reference/html/test-mockmvc.html
https://www.baeldung.com/spring-webappconfiguration
https://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/UnitTest/ImplementsOfUnitTest/UsageOfLibraryForTest.html

投稿2020/09/22 03:06

編集2020/09/22 03:08
montai21

総合スコア17

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問