riku__02
@riku__02

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Spring bootのSecurityについての質問

解決したいこと

現在解体新書という書籍にて11章のSpring セキュリティの内容に着手しています。

その中でもユーザ登録にてパスワードのハッシュ化、ユーザデータにてログイン出来ることの確認がやりたいことなのですが、以下のような動きとなっています。

・ユーザ登録してもパスワードがハッシュ化されない
image.png

・ユーザデータでログインしてもログインできない

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);
    }
0

1Answer

UserServiceImpl.javaのsignupメソッドの処理順が原因ではないでしょうか。

mapper.insertOne(user);(DBへの登録)後にuser.setPassword(encoder.encode(rawPassword));(パスワード文字列暗号化)
をやっても、暗号化処理前の文字列がDBに登録される気がしました。

	/** ユーザー登録 */
	@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));
	}
1Like

Comments

  1. @riku__02

    Questioner

    @eno49conan さんこんばんは!
    いつもご回答ありがとうございます。。
    ご指摘の通り処理順序を入れ替えた所、パスワードのハッシュ化、登録後のユーザーでログインできることを確認できました!

    ご指摘されたら確かに、、と初歩的すぎて恥ずかしくなりました。
    書籍に固執しすぎていて、書籍は正しいという考えを捨てようと思います。
    改修時点のソースコミットして進めます。

    	/** ユーザー登録 */
    	@Override
    	public void signup(MUser user) {
    
    //パスワード暗号化
    		String rawPassword = user.getPassword();
    		user.setPassword(encoder.encode(rawPassword));
    
    		user.setDepartmentId(1); // 部署
    		user.setRole("ROLE_GENERAL"); // ロール
    		mapper.insertOne(user);
    	}
    
  2. 解決してよかったです!

  3. @riku__02

    Questioner

    @eno49conanさん
    おかげさまです🙇‍♂️
    ありがとうございました!!

Your answer might help someone💌