Spring bootのSecurityについての質問
Q&A
Closed
解決したいこと
現在解体新書という書籍にて11章のSpring セキュリティの内容に着手しています。
その中でもユーザ登録にてパスワードのハッシュ化、ユーザデータにてログイン出来ることの確認がやりたいことなのですが、以下のような動きとなっています。
・ユーザデータでログインしてもログインできない
Spring Security 5.4〜6.0でセキュリティ設定の書き方が大幅に変わったという記事を拝見しました。
https://qiita.com/suke_masa/items/908805dd45df08ba28d8
従って、書籍のサンプルソースでは実装できないといった状態です。
Spring Securityの変更後書き方を調べながら進めていますが、上記2点の内容が実施出来ていません。
解決策ありましたら、ご指摘よろしくおねがいします。
関連がありそうなファイル
■SecurityConfig.java
package com.example.demo.config;
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.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
public class SecurityConfig {
@Autowired
private UserDetailsService userDetailsService;
// パスワードの暗号化
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/** セキュリティの対象外を設定 */
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().requestMatchers(
new AntPathRequestMatcher("/webjars/**"), new AntPathRequestMatcher("/css/**"),
new AntPathRequestMatcher("/js/**"), new AntPathRequestMatcher("/h2-console/**"));
}
/** セキュリティの各種設定 */
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// ログイン不要ページの設定
http
.authorizeHttpRequests()
.requestMatchers("/login").permitAll() //直リンクOK
.requestMatchers("/user/signup").permitAll() //直リンクOK
.anyRequest().authenticated(); // それ以外は直リンクNG
//ログイン処理
http.formLogin()
.loginProcessingUrl("/login")//ログイン処理のパス
.loginPage("/login")//ログインページの指定
.failureUrl("/login?error")//ログイン失敗時の遷移先
.usernameParameter("userId")//ログインページのユーザーID
.passwordParameter("password")//ログインページのパスワード
.defaultSuccessUrl("/user/list", true);//成功後の遷移先
// CSRF対策を無効に設定(一時的)
http.csrf().disable();
return http.build();
}
/** 認証の設定 */
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = passwordEncoder();
// インメモリ認証
/*
auth
.inMemoryAuthentication()
.withUser("user") // userを追加
.password(encoder.encode("user"))
.roles("GENERAL")
.and()
.withUser("admin") // adminを追加
.password(encoder.encode("admin"))
.roles("ADMIN");
*/
// ユーザーデータ認証
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder);
}
}
■UserDetailsServiceImpl.java
package com.example.demo.domain.user.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 com.example.demo.domain.user.model.MUser;
import com.example.demo.domain.user.service.UserService;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserService service;
public UserDetailsServiceImpl(@Lazy UserService service) {
this.service = service;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//ユーザー情報取得
MUser loginUser = service.getLoginUser(username);
//ユーザーが存在しない場合
if(loginUser == null) {
throw new UsernameNotFoundException("user not found");
}
//権限List作成
GrantedAuthority authority = new SimpleGrantedAuthority(loginUser.getRole());
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(authority);
//UserDetails生成
UserDetails userDetails = (UserDetails) new User(loginUser.getUserId(),
loginUser.getPassword(),
authorities);
return userDetails;
}
}
■UserServiceImpl.java
package com.example.demo.domain.user.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.domain.user.model.MUser;
import com.example.demo.domain.user.service.UserService;
import com.example.demo.repository.UserMapper;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper mapper;
@Autowired
private PasswordEncoder encoder;
/** ユーザー登録 */
@Override
public void signup(MUser user) {
user.setDepartmentId(1); // 部署
user.setRole("ROLE_GENERAL"); // ロール
mapper.insertOne(user);
//パスワード暗号化
String rawPassword = user.getPassword();
user.setPassword(encoder.encode(rawPassword));
}
/** ユーザー取得 */
@Override
public List<MUser> getUsers(MUser user) {
return mapper.findMany(user);
}
/** ユーザー取得(1件)*/
@Override
public MUser getUserOne(String userId) {
return mapper.findOne(userId);
}
/** ユーザー更新(1件) */
@Transactional
@Override
public void updateUserOne(String userId,
String password,
String userName) {
//パスワード暗号化
String encryptPassword =(encoder.encode(password));
mapper.updateOne(userId, encryptPassword, userName);
// 例外を発生させる
//int i = 1/0;
}
/** ユーザー削除(1件) */
@Override
public void deleteUserOne(String userId) {
mapper.deleteOne(userId);
}
/**ログインユーザー情報取得*/
@Override
public MUser getLoginUser(String userId) {
return mapper.findLoginUser(userId);
}
}
自分で試したこと
SecurityConfig.javaの以下の部分が機能していないんじゃないかとは思っています。。
誤りでしたら指摘ください。もう少しspring securityに関する内容も調べてはみます。。
しかし時間をかけても解決していない状態なので、可能であればアドバイスよろしくお願いいたします。
/** 認証の設定 */
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = passwordEncoder();
// インメモリ認証
/*
auth
.inMemoryAuthentication()
.withUser("user") // userを追加
.password(encoder.encode("user"))
.roles("GENERAL")
.and()
.withUser("admin") // adminを追加
.password(encoder.encode("admin"))
.roles("ADMIN");
*/
// ユーザーデータ認証
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder);
}