2017-08-27 20 views
3

私はItemのためのPOSTメソッドを持っています。各Itemには、作成者のUserのIDが含まれます。そして、私は使用のget著者ID用:(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); しかし、テストでは、これが結果に起因する動作しません:POSTメソッドのspring-bootテストで.getPrincipal()を実行する方法は?

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to ru.pravvich.domain.User

POSTメソッド:

@PostMapping("/get_all_items/add_item_page/add_item") 
public String addItem(@RequestParam(value = "description") final String description) { 

    final User user = (User) SecurityContextHolder 
      .getContext() 
      .getAuthentication() 
      .getPrincipal(); 

    final Item item = new Item(); 
    item.setDescription(description); 
    item.setAuthorId(user.getId()); 
    service.add(item); 
    return "redirect:/get_all_items"; 
} 

とテスト:

@Test 
@WithMockUser(username = "user", roles = "USER") 
public void whenPostThenAdd() throws Exception { 

    Item item = new Item(); 
    item.setDescription("test"); 

    mvc.perform(
      post("/get_all_items/add_item_page/add_item") 
        .param("description", "test") 
    ).andExpect(
      status().is3xxRedirection() 
    ); 

    verify(itemService, times(1)).add(item); 
} 

なぜClassCastExceptionがスローされないのですか?ブラウザフォームからデータを送信しますか?どのようにこの問題を修正しますか?

ありがとうございます。

UPDATE:

@Service 
public class UserService implements UserDetailsService { 

    @Autowired 
    private UserRepository userRepo; 

     @Override 
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
      User user = userRepo.findByUsername(username); 
      if (user == null) user = new User(); 
      return user; 
     } 
} 
+0

可能な複製(https://stackoverflow.com/questions/45905591/why-spring-test-is-fail-does-not -work-mockbean) –

+0

'@ WithUserDetails'を使用することは可能ですか? https://docs.spring.io/spring-security/site/docs/current/reference/html/test-method.html#test-method-withuserdetails –

答えて

3

@WithMockUserは、org.springframework.security.core.userdetails.Userオブジェクトを注入してテストを実行するのに役立ちますが、カスタムユーザーオブジェクトru.pravvich.domain.Userが必要です。

なぜブラウザフォームで動作するのですか?あなたの認証プロセスを元にしたものです。たとえば、自分のru.pravvich.domain.Userを返すために独自のUserDetailsServiceがある場合などです。このプロセスは、Webフォームで自動生成を実行する通常のシナリオで行われますが、テストケースのシナリオ少なくとも、テストケースを設定し、独自の実装について通知することはありません。例を見てみましょう:ユーザーから延び

Aカスタム・ユーザー・クラス(ここでは更新

//MySpring user is just a class that extends from 
    //org.springframework.security.core.userdetails.User 
    //org...security....User implements UserDetailsService that is the 
    //the reason why I can return this class from loadUserByUsername method, you will see it later 
    //it helps me to wrap my own User definition 
    public class MySpringUser extends User { 

    //this is my own user definition, where all my custom fields are defined 
    private MyUser user; 

    //the constructor expect for myUser and then I call the super 
    //constructor to fill the basic fields that are mandatory for spring 
    //security in order to perform the authentication process 
    public MySpringUser(MyUser myUser, Collection<? extends GrantedAuthority> authorities) { 
     super(myUser.getUsername(), myUser.getPassword(), myUser.isEnabled() 
       , true, true, true, authorities); 

     this.setUser(myUser); 
    } 

    public MyUser getUser() { 
     return user; 
    } 

    public void setUser(MyUser user) { 
     this.user = user; 
    } 
    //this is an example of how to get custom fields a custom id for example 
    public Long getId(){ 
     return this.getUser().getId(); 
    } 
} 

AカスタムUserDetailsサービス(ここでは更新

最後に
@Service 
public class MyUserService implements UserDetailsService { 

    @Autowired 
    MyUserRepository myUserRepository; 

    //Principal 
    @Override 
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { 

     //Here I get the User that I defined for my own domain structure 
     MyUser myUser = myUserRepository.findFirstByUsername(s); 

     //Here I return a new User object, in this case MySpringUser is 
     //just a class that extends from User class, its constructor 
     //receive myUser object as parameter in order to get my own custom fields later 
     return new MySpringUser(myUser,AuthorityUtils.createAuthorityList("ADMIN")); 

    } 
} 

テストケースが@WithUserDetailsを使用して、UserDetailsServiceで実装されているloadUserByUsernameメソッドを呼び出す必要があることを通知するその場合、カスタムユーザーオブジェクトにプリンシパルをキャストできます。

@Test 
    @WithUserDetails("myUserNameOnDatabase") 
    public void contextLoads() { 
     try { 
      mockMvc.perform(get("/stores")).andExpect(status().isOk()).andDo(print()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 

これは、キャストが、私はまた私のGitのレポへの完全な例をアップロードしただけで、あなた

@RequestMapping("/stores") 
    public List<Store> getStores(Principal principal){ 

     MySpringUser activeUser = (MySpringUser) ((Authentication) principal).getPrincipal(); 
     System.out.println(activeUser.getUsername()); 

     return storeRepository.findByMyUserId(); 

    } 

(ここでは更新)のように、コントローラで行われ、あなたはそれをダウンロードして試すことができる方法でありますそれはあなたにもっと明確なビューを与えるためにもちろん、これはほんの1つのオプションです。たとえば、拡張Springクラスの代わりに、UserDetailsインターフェイスを実装する独自のクラスを作成することもできます。 。

この代替案がお役に立てば幸いです。

My git Repo example of how use @WithUserDetails

[春のテストが失敗するのはなぜ、@MockBean動作しない]の
+0

ありがとうございます。私は 'loadUserByUsername(String s)'の理解に問題があるかもしれません。あなたはラムダなしで書き直すかもしれませんか?もう一つ質問があります: 'org.springframework.security.core.userdetails.User'のないソリューションへの道はありますか?私の認証システムがクラッシュした後、私は 'security ... User'を使用します。質問をUserServieceバージョンに更新します。それは状況についてのより多くの情報を与えるかもしれない。もう一度ありがとうございます。 – Pavel

+1

ちょうど私に答えを更新する瞬間を与えてください。 –

+0

あなたは更新を見ることができます、それがあなたを助けることを願っています。 –

0

春のセキュリティの@WithMockUserは、あなたがこの例外に遭遇している理由であるorg.springframework.security.core.userdetails.Userオブジェクトとのセキュリティコンテキストを取り込みます。 documentationはこれについてかなり明確である:

元本認証であなたのClassCastExceptionに春のセキュリティのユーザーオブジェクト

一つの可能​​な解決策は、単に春のUserクラスを再利用して、著者を返すようにgetUsername()を使用することですID、またはお客様がUserオブジェクトを購入する必要がある場合は、テスト用のセキュリティコンテキストを設定する別の方法を見つけてください。

関連する問題