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();
}
}
独自のログインフォームを実装するなど、様々な応用が可能できますが、最低限のログイン機能は上記のコードのコピペで使えるはずです。