[解決済み] Spring Test & Security: 認証をモック化する方法とは?
質問
コントローラのURLが適切に保護されているかどうかをユニットテストする方法を考えていました。誰かが何かを変更し、誤ってセキュリティ設定を削除してしまった場合に備えてです。
私のコントローラのメソッドは次のようなものです。
@RequestMapping("/api/v1/resource/test")
@Secured("ROLE_USER")
public @ResonseBody String test() {
return "test";
}
このようにWebTestEnvironmentを設定しました。
import javax.annotation.Resource;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({
"file:src/main/webapp/WEB-INF/spring/security.xml",
"file:src/main/webapp/WEB-INF/spring/applicationContext.xml",
"file:src/main/webapp/WEB-INF/spring/servlet-context.xml" })
public class WebappTestEnvironment2 {
@Resource
private FilterChainProxy springSecurityFilterChain;
@Autowired
@Qualifier("databaseUserService")
protected UserDetailsService userDetailsService;
@Autowired
private WebApplicationContext wac;
@Autowired
protected DataSource dataSource;
protected MockMvc mockMvc;
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected UsernamePasswordAuthenticationToken getPrincipal(String username) {
UserDetails user = this.userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
user,
user.getPassword(),
user.getAuthorities());
return authentication;
}
@Before
public void setupMockMvc() throws NamingException {
// setup mock MVC
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.wac)
.addFilters(this.springSecurityFilterChain)
.build();
}
}
実際のテストでは、次のようなことをやってみました。
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import eu.ubicon.webapp.test.WebappTestEnvironment;
public class CopyOfClaimTest extends WebappTestEnvironment {
@Test
public void signedIn() throws Exception {
UsernamePasswordAuthenticationToken principal =
this.getPrincipal("test1");
SecurityContextHolder.getContext().setAuthentication(principal);
super.mockMvc
.perform(
get("/api/v1/resource/test")
// .principal(principal)
.session(session))
.andExpect(status().isOk());
}
}
ここで拾ったんです。
- http://java.dzone.com/articles/spring-test-mvc-junit-testing ここで
- http://techdive.in/solutions/how-mock-securitycontextholder-perfrom-junit-tests-spring-controller またはこちらをご覧ください。
- Spring MVC Controllerで指定された@PreAuthorizeアノテーションとそのspring ELをJUnitでテストするにはどうすればよいですか?
しかし、よく見ると、これは URL に実際のリクエストを送信しない場合にのみ役立ち、機能レベルでサービスをテストする場合にのみ役立ちます。私の場合、quot;access denied"例外がスローされました。
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.1.RELEASE.jar:3.2.1.RELEASE]
...
次の2つのログメッセージは、基本的に、どのユーザーも認証されなかったということを述べています。
Principal
が動作しなかったか、上書きされた。
14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List test.TestController.test(); target is of class [test.TestController]; Attributes: [ROLE_USER]
14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
解決方法は?
ということが判明しました。
SecurityContextPersistenceFilter
は、Spring Securityのフィルターチェーンの一部で、常に私の
SecurityContext
を呼び出して設定した
SecurityContextHolder.getContext().setAuthentication(principal)
(または
.principal(principal)
メソッド)を使用します。このフィルタは
SecurityContext
を
SecurityContextHolder
を使って
SecurityContext
から
SecurityContextRepository
オーバーライト(OVERWRITING
は先ほど設定したものです。リポジトリは
HttpSessionSecurityContextRepository
をデフォルトで使用します。そのため
HttpSessionSecurityContextRepository
は、与えられた
HttpRequest
にアクセスし、対応する
HttpSession
. もし存在すれば、それは
SecurityContext
から
HttpSession
. これが失敗すると、リポジトリは空の
SecurityContext
.
したがって、私の解決策は
HttpSession
を保持しているリクエストと一緒に
SecurityContext
:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import eu.ubicon.webapp.test.WebappTestEnvironment;
public class Test extends WebappTestEnvironment {
public static class MockSecurityContext implements SecurityContext {
private static final long serialVersionUID = -1386535243513362694L;
private Authentication authentication;
public MockSecurityContext(Authentication authentication) {
this.authentication = authentication;
}
@Override
public Authentication getAuthentication() {
return this.authentication;
}
@Override
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
}
@Test
public void signedIn() throws Exception {
UsernamePasswordAuthenticationToken principal =
this.getPrincipal("test1");
MockHttpSession session = new MockHttpSession();
session.setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
new MockSecurityContext(principal));
super.mockMvc
.perform(
get("/api/v1/resource/test")
.session(session))
.andExpect(status().isOk());
}
}
関連
-
[解決済み] spring-ws: エンドポイントマッピングが見つかりませんでした
-
アーティファクトXXXXが見つからず、Mavenにalibaba.cloudの依存関係を導入することができませんでした。
-
[解決済み] プライベートメソッド、フィールド、インナークラスを持つクラスをテストするにはどうすればよいですか?
-
[解決済み] PHPでSQLインジェクションを防ぐにはどうしたらいいですか?
-
[解決済み] JUnit 4のテストで、ある例外が投げられたことをどのように断言しますか?
-
[解決済み] モックとスタブの違いは何ですか?
-
[解決済み] Spring Test & Security: 認証をモック化する方法とは?
-
[解決済み] Spring 3 RequestMapping。パスの値を取得する
-
[解決済み] プロパティファイルから値を読み込むには?
-
[解決済み] コンストラクタの引数を必要とする Bean を @Autowire する方法はありますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Unit TestからApplicationContextを読み込むのに失敗しました。FileNotFound
-
SpringBoot の例外です。クラスパスリソースに定義された名前 'entityManagerFactory'を持つビーンの作成エラー
-
解決方法 原因:com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException.NonTransientConnectionExceptionが原因です。
-
[解決済み] java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log
-
[解決済み】ビーン初期化完了後にメソッドを呼び出すには?
-
[解決済み】データソースの設定に失敗しました:'url'属性が指定されておらず、埋め込まれたデータソースが設定できませんでした。
-
[解決済み】Spring MVCのApplicationContextとWebApplicationContextの違いは何ですか?
-
[解決済み] サーブレットにおける <mvc:annotation-driven /> と <context:annotation-config /> の違いは何ですか?
-
[解決済み] スコープ("prototype")ビーンスコープで新しいビーンが作成されない
-
[解決済み] Spring Frameworkにおける依存性注入と制御の逆転とは?