前回の続きです。
前回作ったログイン画面をDBと連携させていきます。
開発環境
・Java:17
・SpringBoot:2.7.4
・前回の記事までの実装(詳しくは前回の記事見てください)
・DBに接続して動作確認が取れているSpringBootのプロジェクト
テーブルの準備
まずはユーザー認証に使用するテーブルを作成します。
今回は必要最低限の構成にしました。
テーブル名はなんでもいいので分かりやすい名前にしてください。
必要に応じてカスタマイズしてみてください。
テーブルにデータを追加
適当にテストデータを登録します。
ここで気を付けて欲しいことはパスワードはハッシュ化した値を格納することです。
ハッシュ化する際はこちらのサイトがおすすめです。
entityクラスを作成
次はテーブルから取得した値を格納しておくためのクラスを作ります。
lombokを使用していない場合は@Dataを外してゲッター、セッターも記載してください。
package com.example.demo.data;
import org.springframework.stereotype.Repository;
import lombok.Data;
@Repository
@Data
public class LoginUser {
private int id;
private String userName;
private String password;
private String email;
}
テーブルからユーザー名とパスワードを取得するDaoを作成
テーブルからユーザー名で検索してパスワードを取得するような機能を作ってください。
細かい実装はなんでも大丈夫です。
package com.example.demo.dao;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.example.demo.data.LoginUser;
@Component
public class LoginUserDao {
@Autowired
private JdbcTemplate jdbc;
/**
* ユーザ検索するメソッド
* @param userName
* @return
*/
public LoginUser searchByUserName(String userName) {
String query = "SELECT * FROM login_user WHERE user_name like ?";
//SQL実行
List<Map<String, Object>> list = jdbc.queryForList(query, userName);
return getLoginUser(list);
}
/**
* 検索結果をLoginUserクラスに詰める
* @param result
* @return
*/
private LoginUser getLoginUser(List<Map<String, Object>> result) {
LoginUser loginUser = new LoginUser();
for(int i = 0; i < result.size(); i++) {
int id = (Integer)result.get(i).get("id");
String userName = (String)result.get(i).get("user_name");
String password = (String)result.get(i).get("password");
String email = (String)result.get(i).get("email");
loginUser.setId(id);
loginUser.setEmail(userName);
loginUser.setPassword(password);
loginUser.setEmail(email);
}
return loginUser;
}
}
ここからが本番
ここからが本番です。
- UserDetailsをの実装
- UserDetailsServiceの実装
これらをやっていきます。
何をやるかというと自分で作ったテーブルのカラム名をSpringSecurityが認識できるように設定していくイメージです。
それではいきます。
UserDetailsの実装
自分で作ったエンティティのフィールドとSpringSecurityで必要となる項目をマッピングしていくイメージです。
コンストラクタに先ほど作成したエンティティクラスを設定しましょう。
その後getUsernameとgetPasswordでエンティティオブジェクトからユーザー名とパスワードをそれぞれ取得した値を返すように実装します。
ちなみに今回使用するのはユーザー名とパスワードのみですが他にもいろいろな設定ができます。
他の項目もいつか解説記事書く予定です。
package com.example.demo.service.impl;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import com.example.demo.data.LoginUser;
@Service
public class CustomUserDetails implements UserDetails {
//自分で作成したLoginUserをフィールドに持たせる
private final LoginUser user;
/**
* コンストラクタ
* @param user
*/
public CustomUserDetails(LoginUser user) {
this.user = user;
}
/**
* ロールの取得(今回は使わないのでnullをリターン)
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
/**
* パスワードの取得
* ログインユーザーのパスワードをGetする
*/
@Override
public String getPassword() {
return this.user.getPassword();
}
/**
* ユーザー名の取得
* ログインユーザーの名前をGetする
*/
@Override
public String getUsername() {
return this.user.getUserName();
}
/**
* アカウントが有効期限でないか(使わないので常にtrue)
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* アカウントがロックされていないか(使わないので常にtrue)
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 認証情報が有効期限切れでないか(使わないので常にtrue)
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* アカウントが有効であるかどうか(使わないので常にtrue)
*/
@Override
public boolean isEnabled() {
return true;
}
}
UserDetaisServiceの実装
先ほど作ったDaoを使ってDBからデータを取得し、エンティティオブジェクトに設定、それをもとにUserDetailsを作成して返します。
package com.example.demo.service.impl;
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;
import com.example.demo.dao.LoginUserDao;
import com.example.demo.data.LoginUser;
/**
* ログインユーザー検索サービスクラス
* @author masahiro suzuki
*
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private LoginUserDao dao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LoginUser user= dao.searchByUserName(username);
if(user == null) {
throw new UsernameNotFoundException("not found :" + username);
}
return new CustomUserDetails(user);
}
}
SecurityConfigにPasswordEncoderを追加
パスワードをハッシュ化するためにPasswordEncoderを追加します。
package com.example.demo.config;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin()
.and()
.logout()
.and()
.authorizeHttpRequests(authz -> authz
.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
.permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
実行してみよう
これで完成です。
それでは適当な画面にアクセスしてみましょう。
ログイン画面が表示されるのでUserName、Passwordは先ほどテーブルに追加したものを使用してください。
認証に成功したら画面が表示されます。
お疲れ様でした。
コメント