Springを用いた開発では、Spring Securityを使えばログイン機能が簡単に実装できます。
今回は最低限のコード量で、フォームでのログインを実装します。
build.gradleの修正
まずは、Spring Securityを使うため、build.gradle
に依存ライブラリを追加します。
以下のように、spring-boot-starter-security
を追加すればOKです。gradleでなくmavenを使っている場合も同様です。
dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' }
ユーザークラスを実装
ユーザーを識別するため、ユーザークラスを実装します。
まずは、データベースにuserテーブルを作成します。 テーブルを作成するSQL文は以下の通りです。
CREATE TABLE `user` ( `id` int PRIMARY KEY AUTO_INCREMENT, `username` varchar(20) NOT NULL UNIQUE, `password` varchar(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8
username
にはユーザー名を、password
にはハッシュ化されたパスワードを格納します。
なお、レコード名は「username」と「password」にしてください。(違う名前でもいいですが、記述するコード量が少し増えます。)
次に、このテーブルに対応するEntityクラスを作成します。
ポイントは、UserDetails
インターフェースを継承する点です。そのため、いくつかのメソッドをオーバーライドしないといけません。
import java.util.Collection; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Getter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @Getter @Entity @Table(name = "user") public class User implements UserDetails { @Id private String id; private String username; private String password; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
ユーザークラスを扱うレポジトリクラス・サービスクラスの作成
つぎに、上で作成したユーザークラスを扱うレポジトリクラスを作成します。
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, String> { User findByUsername(String username); }
↑で作成したレポジトリクラスを使用するサービスクラスも作成します。
ポイントは、UserDetailsService
インターフェースを継承し、loadUserByUsername()
メソッドをオーバーライドする点です。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserDetailsService { @Autowired private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } }
WebSecurityConfigurerAdapterクラスを継承するクラスの作成
最後に、WebSecurityConfigurerAdapterという抽象クラスを継承するクラスを実装します。
今回は、トップページには誰でもアクセスでき、それ以外はログインが必要で、ログイン成功後は/memberというパスに遷移する、という内容とします。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private final UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(this.userDetailsService); } // cssや画像などを読み込む場合は記述 @Override public void configure(WebSecurity web) throws Exception { //静的リソースをセキュリティ対象外に設定 web.ignoring().antMatchers("/css/**", "/js/**", "/img/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated() .and() // ログインにはデフォルトのフォームを使用 .formLogin() // ログイン後に遷移するパスを記述 .defaultSuccessUrl("/member", true) .and() .logout() // ログアウト後に遷移するパスを記述 .logoutSuccessUrl("/"); } // これを記述することで、フォームから入力されたパスワードをハッシュ化したものが、DBに格納されたパスワードと照合されます。 @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
独自のログインフォームを実装するなど、様々な応用が可能できますが、最低限のログイン機能は上記のコードのコピペで使えるはずです。