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

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

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

Security+は、IT業界団体であるCompTIA認定の資格の一つです。ネットワークセキュリティやコンプライアンスと運用セキュリティといったセキュリティ分野における知識・技術の証明になり、セキュリティインシデントに対応するための知識も評価されます。

Spring Security

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

Spring

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

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

spring securityで認証されたユーザーのidを取得したいのですがnullが返ってくる

shinichi-ito
shinichi-ito

総合スコア9

Security+

Security+は、IT業界団体であるCompTIA認定の資格の一つです。ネットワークセキュリティやコンプライアンスと運用セキュリティといったセキュリティ分野における知識・技術の証明になり、セキュリティインシデントに対応するための知識も評価されます。

Spring Security

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

Spring

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

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

1回答

0グッド

0クリップ

7391閲覧

投稿2020/02/06 22:32

編集2020/02/07 13:46

spring securityで認証されたユーザーのidを取得したいのですがnullが返ってきます

spring securityで認証はできています。
認証後の偏移画面で

Authentication auth = SecurityContextHolder.getContext().getAuthentication(); //Principalからログインユーザの情報を取得 String userName = auth.getName();

にてログインユーザーの名前は取得できています。
さらにログインユーザーのidも取得したいです。

そのために「UserDetailsインターフェース」を実装したクラスを作成しidを取得しようとしています。
参考にしたサイトはこちら

該当コードを下記にまとめます。

Entity

@Entity @Table(name = "user") @Data public class LoginUser { @Column(name = "user_id") @Id private int userId; @Column(name = "user_name") private String userName; @Column(name = "password") private String password; }

UserDetailsインターフェースの実装コード
このコード内で「LoginUser」からidを取得する独自メソッドを設定しています。

public class UserDetailsImpl implements UserDetails { private static final long serialVersionUID = 1L; private LoginUser user; protected UserDetailsImpl() {}; public UserDetailsImpl(LoginUser user) { this.user=user; } public int getUserId() { return user.getUserId(); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public String getPassword() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public String getUsername() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public boolean isAccountNonExpired() { // TODO 自動生成されたメソッド・スタブ return false; } @Override public boolean isAccountNonLocked() { // TODO 自動生成されたメソッド・スタブ return false; } @Override public boolean isCredentialsNonExpired() { // TODO 自動生成されたメソッド・スタブ return false; } @Override public boolean isEnabled() { // TODO 自動生成されたメソッド・スタブ return false; } }

controllerクラス内のメソッドに引数として「@AuthenticationPrincipal UserDetailsImpl userDetails」を設定しています。
そして「System.out.println(userDetails.getUserId());」でユーザーidを取得しようとしていますがnullが返ってきます。

@Controller public class HomeController { // インスタンスを作成しDIコンテナに格納。 @Autowired UserService userService; //userListのアドレスにアクセスした際にGetメソッドを実行。 @GetMapping("/userList") public String getUserList(Model model,@AuthenticationPrincipal UserDetailsImpl userDetails) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); //Principalからログインユーザの情報を取得 String userName = auth.getName();//ユーザー名は取得できます。 System.out.println(userDetails.getUserId()); // userIdは nullが返ってきます。 //@Autowiredで作成したインスタンスを元に、userServiceのメソッドを呼び出す。 List<User> userList = userService.selectMany(); //userServiceから受け取ったデータをView側に渡す。 model.addAttribute("userList", userList); model.addAttribute("userName", userName); //template配下のファイル名を指定することでViewを呼び出せる。 return "userList"; } }

何が原因かわからず詰まっております。
何卒よろしくお願いいたします。


編集、追記依頼をいただきましたのでお言葉に甘えて追記しました。

UserDetailsインターフェースの実装コード内のisEnabledメソッドをtrueに変更しました。

public class UserDetailsImpl implements UserDetails { private static final long serialVersionUID = 1L; private LoginUser user; protected UserDetailsImpl() {}; public UserDetailsImpl(LoginUser user) { this.user=user; } public int getUserId() { return user.getUserId(); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public String getPassword() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public String getUsername() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public boolean isAccountNonExpired() { // TODO 自動生成されたメソッド・スタブ return true; } @Override public boolean isAccountNonLocked() { // TODO 自動生成されたメソッド・スタブ return true; } @Override public boolean isCredentialsNonExpired() { // TODO 自動生成されたメソッド・スタブ return true; } @Override public boolean isEnabled() { // TODO 自動生成されたメソッド・スタブ return true; }

UserDetailsServiceインターフェースの実装クラス

@Service public class UserDetailsServiceImpl implements UserDetailsService{ @Autowired private LoginUserDao userDao; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { LoginUser user = userDao.findUser(userName); if (user == null) { throw new UsernameNotFoundException("User" + userName + "was not found in the database"); } List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>(); GrantedAuthority authority = new SimpleGrantedAuthority("USER"); grantList.add(authority); //rawDataのパスワードは渡すことができないので、暗号化 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); //UserDetailsはインタフェースなのでUserクラスのコンストラクタで生成したユーザオブジェクトをキャスト UserDetails userDetails = (UserDetails)new User(user.getUserName(), encoder.encode(user.getPassword()),grantList); return userDetails; } }

セキュリティコンフィグレーションクラス

@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsService; //フォームの値と比較するDBから取得したパスワードは暗号化されているのでフォームの値も暗号化するために利用 @Bean public BCryptPasswordEncoder passwordEncoder() { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); return bCryptPasswordEncoder; } /** * 認可設定を無視するリクエストを設定 * 静的リソース(image,javascript,css)を認可処理の対象から除外する */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers( "/images/**", "/css/**", "/javascript/**" ); } /** * 認証・認可の情報を設定する * 画面遷移のURL・パラメータを取得するname属性の値を設定 * SpringSecurityのconfigureメソッドをオーバーライドしています。 */ @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/home").permitAll() // アクセス制限のないURL .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") //ログインページはコントローラを経由しないのでViewNameとの紐付けが必要 .loginProcessingUrl("/sign_in") //フォームのSubmitURL、このURLへリクエストが送られると認証処理が実行される .usernameParameter("username") //リクエストパラメータのname属性を明示 .passwordParameter("password") .defaultSuccessUrl("/userList", true) //認証が成功した際に遷移するURL .failureUrl("/login?error") //認証が失敗した際に遷移するURL .permitAll() //どのユーザでも接続できる。 .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/login?logout") .permitAll(); } @Autowired public void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }

mavenではなくてgradleを使用しています。
build gradle

dependencies { compile('org.springframework.boot:spring-boot-starter-data-jpa') implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'mysql:mysql-connector-java' annotationProcessor 'org.projectlombok:lombok' implementation 'org.springframework.boot:spring-boot-starter-security' // https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5', version: '3.0.4.RELEASE' // https://mvnrepository.com/artifact/javax.persistence/javax.persistence-api compile group: 'javax.persistence', name: 'javax.persistence-api', version: '2.2' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } }

他のコードも必要でしたら追記いたします。

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

rubytomato

2020/02/06 23:07

2点ご確認ください。 1) 参考にされた記事のさらにリンク先記事で説明されているServiceクラスは実装されていますか? 2) 質問に記載のUserDetailsImplのisEnabledメソッドがfalseを返しています。有効なアカウントであればtrueを返す必要があります。 上記の確認でまだ認証情報を取得できないようであれば、UserDetailsServiceインターフェースの実装クラス、セキュリティコンフィグレーションクラス(WebSecurityConfigureAdapterを継承した)のソースコードと、pom.xmlの内容を質問に追記ください。
asahina1979

2020/02/06 23:26

認証が不要なエリアや、競合するアノテーションがある場合はnullやね
asahina1979

2020/02/07 13:46

プレビューみながらマークダウンの有効無効を確認しながら編集してください
shinichi-ito

2020/02/07 13:50

rubytomato様、asahina1979様 ご丁寧に対応いただきありがとうございます。 ご指摘いただいたUserDetailsImplのisEnabledメソッドをtrueにしましたが、やはりnullになってしまい 必要コードを追記させていただきました。

回答1

0

ベストアンサー

とりあえず最もアウトな点

public class UserDetailsImpl implements UserDetails { private static final long serialVersionUID = 1L; private LoginUser user; protected UserDetailsImpl() {}; public UserDetailsImpl(LoginUser user) { this.user=user; } public int getUserId() { return user.getUserId(); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO 自動生成されたメソッド・スタブ return null; }

ここまでは問題ない(はず)

@Override public String getPassword() { // TODO 自動生成されたメソッド・スタブ return null; }

入力されたパスワードは null とは一致しないのでミスマッチ二なります。

@Override public String getUsername() { // TODO 自動生成されたメソッド・スタブ return null; }

入力されたユーザー名は null とは一致しないのでミスマッチ二なります。

@Override public boolean isAccountNonExpired() { // TODO 自動生成されたメソッド・スタブ return false; }

false なので有効期限切れアカウントとなります。

@Override public boolean isAccountNonLocked() { // TODO 自動生成されたメソッド・スタブ return false; }

false なのでロック中のアカウントになります。

@Override public boolean isCredentialsNonExpired() { // TODO 自動生成されたメソッド・スタブ return false; }

false なのでパスワードの有効期限切れとなります。

@Override public boolean isEnabled() { // TODO 自動生成されたメソッド・スタブ return false; }

false なのでアカウントは無効となります。

}

とりあえず、ぱっと見最初にダメなところは以上

追記

//UserDetailsはインタフェースなのでUserクラスのコンストラクタで生成したユーザオブジェクトをキャスト UserDetails userDetails = (UserDetails)new User(user.getUserName(), encoder.encode(user.getPassword()),grantList);

暗号化済みのパスワードを暗号化してる?それだとミスマッチですね

投稿2020/02/07 13:52

編集2020/02/07 14:31
asahina1979

総合スコア8154

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

2020/02/08 01:52

こちらの回答が他のユーザーから「過去の低評価」という指摘を受けました。

回答へのコメント

shinichi-ito

2020/02/07 14:07

asahina1979様 アドバイスありがとうございます。 早速訂正させていただきました。 public class UserDetailsImpl implements UserDetails { private static final long serialVersionUID = 1L; private LoginUser user; protected UserDetailsImpl() {}; public UserDetailsImpl(LoginUser user) { this.user=user; } public int getUserId() { return user.getUserId(); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO 自動生成されたメソッド・スタブ return null; } @Override public String getPassword() { return this.user.getPassword(); } @Override public String getUsername() { return this.user.getUserName(); } @Override public boolean isAccountNonExpired() { // TODO 自動生成されたメソッド・スタブ return true; } @Override public boolean isAccountNonLocked() { // TODO 自動生成されたメソッド・スタブ return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } このようでよいのでしょうか? それでもnullが返ってきてしまうんです。
shinichi-ito

2020/02/07 22:08

asahina1979様 遅い時間まで回答くださり大変感謝しております。 現状まだ解決しておりませんが、いろいろとアドバイス頂いたことをしっかりと把握し 時間をかけてコード全体を見直してみようと思います。 asahina1979様、rubytomato様 貴重な時間を割いていただきほんとに感謝しております。 ありがとうございます。 解決した際には、再度ご報告させていただきます。

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

ただいまの回答率
86.02%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Security+

Security+は、IT業界団体であるCompTIA認定の資格の一つです。ネットワークセキュリティやコンプライアンスと運用セキュリティといったセキュリティ分野における知識・技術の証明になり、セキュリティインシデントに対応するための知識も評価されます。

Spring Security

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

Spring

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

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。