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

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

新規登録して質問してみよう
ただいま回答率
85.37%
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

解決済

1回答

11600閲覧

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

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プロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

1クリップ

投稿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' } }

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

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

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

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

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

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

rubytomato

2020/02/06 23:07

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

退会済みユーザー

2020/02/06 23:26

認証が不要なエリアや、競合するアノテーションがある場合はnullやね
退会済みユーザー

退会済みユーザー

2020/02/07 13:46

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

2020/02/07 13:50

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

回答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
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

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様 貴重な時間を割いていただきほんとに感謝しております。 ありがとうございます。 解決した際には、再度ご報告させていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問