【Spring Security】DBから認証情報を取得して認証するのをやってみる
DBから認証情報を取得して認証する場合はSpring Securityを使ってカスタムの認証プロバイダ(AuthenticationProvider)を実装することが一般的です
この認証プロバイダはユーザー名とパスワードを取得し、DBに格納された認証情報と照合して認証を行います
以下にDBから認証情報を取得して認証するための実装例を紹介します
1. こんなテーブルを準備
まず、ユーザー情報を格納するためのデータベーステーブルを準備します
例えば、usersテーブルを作成し、ユーザー名とパスワード(暗号化されていることが一般的)を保存します
CREATE TABLE users (
username VARCHAR(50) PRIMARY KEY,
password VARCHAR(100)
);
2. UserDetailsServiceの実装
次に、Spring SecurityのUserDetailsServiceを実装して、DBからユーザー情報を取得するクラスを作成します
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
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;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private DataSource dataSource;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try (Connection conn = dataSource.getConnection()) {
PreparedStatement stmt = conn.prepareStatement("SELECT username, password FROM users WHERE username = ?");
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
String dbUsername = rs.getString("username");
String dbPassword = rs.getString("password");
return User.withUsername(dbUsername)
.password(dbPassword)
.roles("USER") // ロールを設定(必要に応じて)
.build();
} else {
throw new UsernameNotFoundException("User not found with username: " + username);
}
} catch (SQLException e) {
throw new RuntimeException("Error accessing database", e);
}
}
}
このCustomUserDetailsServiceクラスではUserDetailsServiceを実装してloadUserByUsernameメソッドをオーバーライドしています
このメソッド内でDBからユーザー情報を取得し、UserDetailsオブジェクトを構築して返します
3. SecurityConfigの設定
SecurityConfigクラスでカスタムのUserDetailsServiceを設定します
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
configure(AuthenticationManagerBuilder auth)メソッドで、CustomUserDetailsServiceを設定し、passwordEncoder()メソッドでパスワードエンコーダー(例えばBCryptPasswordEncoder)を設定します
このようにして実装していきます
4. Spring Securityで投げられる例外とHTTPレスポンス
UsernameNotFoundException
ユーザー名が見つからない場合にスローされます
デフォルトのHTTPレスポンスコードは401 (Unauthorized)です
BadCredentialsException
資格情報が無効な場合(例えば、間違ったパスワード)にスローされます
デフォルトのHTTPレスポンスコードは401 (Unauthorized)です
その他の認証例外
その他の認証関連の例外(例えば、アカウントがロックアウトされている場合の LockedException など)に対しても、デフォルトのHTTPレスポンスコードは401 (Unauthorized)です
アクセス拒否(AccessDeniedException)
認証は成功したが、リソースにアクセスする権限がない場合にスローされます
デフォルトのHTTPレスポンスコードは403 (Forbidden)です
これらのデフォルトの動作はカスタマイズ可能
exceptionHandling()メソッドを使用してカスタムのエラーハンドリングやエラーページを設定することで、異なるレスポンスを返すことができます
以下は実装例です
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied") // アクセス拒否時のページ
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); // 認証エラー時のHTTPステータス
}
上記の例では、.accessDeniedPage(“/access-denied”)によってアクセス拒否時にはカスタムのエラーページ/access-deniedにリダイレクトされます
また、.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))によって認証エラー時にはデフォルトのHTTPステータスコードとして401 (Unauthorized)が返されるように設定されています
いかがでしたでしょうか?
他にもいろんなことができます
是非参考ください
是非フォローしてください
最新の情報をお伝えします