前回の記事でログイン機能の作成を実施しましたが、ログインユーザは「User」で固定。パスワードはコンソールに出力と、実際の運用には程遠い内容。
今回はDBに登録されたユーザ名(ID)と、パスワードを照合していきます。
テーブルの準備
今回使用するテーブルはこちら
spring securityの学習がメインなので詳細は割愛しますが、account-idはcharacterにすればよかった。
account_lock〜account_groupまでは今回は使用しませんが、いずれこの辺も使用する予定
telephone1と2はユーザID、パスワードの他に、もう1点(合計3点一致)で認証とする場合に使用する予定
テーブルにデータを追加
id:12345
password:password
で登録しておきます。
※パスワードはハッシュ化した値を格納します。
ハッシュ化はこのサイトで実施できます。
ハッシュ化についての説明はこちらの記事を読んでください
select account_id,password from user_account;
テーブルから取得した値を格納するentityを作成
package com.example.entity;
public record UserAccount(int accountId,String password) {}
テーブルからaccount_idとpasswordを取得するmapperを作成
- Java
package com.example.mapper;
import org.apache.ibatis.annotations.Mapper;
import com.example.entity.UserAccount;
@Mapper
public interface DemoMapper {
public UserAccount getAccount(int accountId);
}
- xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.DemoMapper">
<select id="getAccount" resultType="com.example.entity.UserAccount">
SELECT
account_id,
password
FROM
user_account
WHERE
account_id = #{accountId}
</select>
</mapper>
今回のメインのひとつ UserDetailsの実装
UserDetailsインターフェースを実装します。
ポイント1:コンストラクタを作成し、UserAccountを設定します。
ポイント2:getUsernameとgetPasswordに上記で設定したUserAccountから取得した値を返すよう実装します。
その他は今回は使用しないのでis〜のメソッドは「true」を返却する。
注意:自動生成された時点ではFalseを返却するようになってます。Falseではアカウントロック状態等になってしまうので、Trueを返却するように変更します。
package com.example.auth;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.example.entity.UserAccount;
public class DemoUserDetails implements UserDetails {
private final UserAccount user ;
public DemoUserDetails(UserAccount user) {
this.user =user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override
public String getPassword() {
return this.user.password();
}
@Override
public String getUsername() {
return Integer.valueOf(user.accountId()).toString();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
もう一つのメイン UserDetailsServiceの実装
ポイント1:mapperを使用し、Dbからデータを取得し、UserAccountに設定する。
※アカウントIDをintで作成してしまったため、型変換が必要。ん〜やっぱりcharacterで作成すべきだった・・・
ポイント2:取得結果が0件(ユーザが存在しない)の場合、「UsernameNotFoundException」をthrowする
ポイント3:取得したUserAccountを指定して、UserDetailsを作成する
package com.example.auth;
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.entity.UserAccount;
import com.example.mapper.DemoMapper;
@Service
public class DemoUserDetailsService implements UserDetailsService {
@Autowired
DemoMapper mapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserAccount user = mapper.getAccount(Integer.valueOf(username));
if (user == null) {
throw new UsernameNotFoundException("not found : " + username);
}
return (new DemoUserDetails(user));
}
}
最後にSecurityConfigを修正
ポイント:passwordEncoderを追加
package com.example.conf;
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
public SecurityFilterChain securityFilterChain(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();
}
}
実行してログインしてみる
http://localhost:8080/toppage にアクセスするとログイン画面が表示されるので
UserNameに12345
Passwordにpassword を入力