1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Spring Security + MyBatis ログイン機能作成メモ

Last updated at Posted at 2023-08-06

Eclipseで Spring スタータープロジェクト作成後、MyBatisでクラス自動作成

spring-boot-starter-securityのバージョンが上がり、WebSecurityConfigurerAdapterクラスが使えなくなったため整理。

  • generatorConfig.xml
    手動でやってるので若干違うかも・・・
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration >
  <!-- mysql connector -->
  <classPathEntry
      location="/Users/[USER]/.m2/repository/mysql/mysql-connector-java/8.0.23/mysql-connector-java-8.0.23.jar" />

  <context id="context1" >
  
    <!-- JDBC -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
      connectionURL="jdbc:mysql://localhost:3306/TestDB"
      userId="root" password="">

      <!-- Table Configuration matched more than one table 対策 -->
      <!-- http://www.mybatis.org/generator/usage/mysql.html -->
      <property name="nullCatalogMeansCurrent" value="true"/>
      
    </jdbcConnection>
    
      <!-- 自動生成するエンティティ -->

      <javaModelGenerator
          targetPackage="com.example.demo.entity"
          targetProject="src/main/java/"
      />
      <sqlMapGenerator
          targetPackage="com.example.demo.entity"
          targetProject="src/main/java/"
      />
      <javaClientGenerator
          targetPackage="com.example.demo.entity"
          targetProject="src/main/java/"
          type="XMLMAPPER"
      />

    <!-- 自動生成対象のテーブル名 -->
    <table tableName="LoginUser" domainObjectName="LoginUser">
      <generatedKey column="id" sqlStatement="JDBC"/> <!-- auto increament 余計なXMLが入ってしまうため不要かも? -->
    </table>

  </context>
</generatorConfiguration>

テスト用の対象テーブル

CREATE TABLE IF NOT EXISTS LoginUser (
  id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(64) NOT NULL,
  password VARCHAR(128) NOT NULL
);

ライブラリのロードとセキュリティ用のクラス作成

 以下のサンプルはアノテーションとimplementsが正しければクラス名はなんでもいいです。

-pom.xml

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity6</artifactId>
		</dependency>
  • SecurityConfig.java
    ユーザー作成の時にパスワードをエンコードした処理をBCryptPasswordEncoderで定義しておく
    これがないとユーザー認証ができないので注意
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean //パスワードのエンコード
    BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

	@Bean
	SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	    
		//ログイン処理の実装
		http.formLogin(login -> login //フォーム認証の設定
		    .loginProcessingUrl("/login") //ログイン処理のパス
			.loginPage("/login") //ログインページの指定
			.defaultSuccessUrl("/hello") //ログイン成功後の遷移先(任意で「/hello」とする)
			.failureUrl("/login?error=true") //ログイン失敗時の遷移先(任意で「/login?error」とする)
			.permitAll()
		).logout(logout -> logout //ログアウトの設定記述開始
			.logoutSuccessUrl("/login") //ログアウト成功後のリダイレクト先URL
		).authorizeHttpRequests(authz -> authz //URLごとの認可設定
			.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
			.permitAll() //css,jsなどへのアクセス許可
		    .anyRequest().authenticated() //ログインページ以外のURLへはログイン後のみアクセス可能
		);
		
		return http.build();
	}
}

SecurityFilterChainがログイン時に自動的に実行される

認証用クラスの作成

UserDetailsクラスをimplementsして作成
ログイン後のこのクラスのインスタンスが保持される
${#authentication.principal} でテンプレート内からアクセスできる

public class AuthUserDetails implements UserDetails {

	// 認証するユーザー情報
	private final LoginUser loginUser;
	private final Collection<? extends GrantedAuthority> authorities;
	
	// コンストラクタ 認証したい任意のユーザー情報を指定
	public AuthUserDetails(LoginUser loginUser) {
		this.loginUser = loginUser;
		// 仮の処理
		List<String> list = new ArrayList<String>(Arrays.asList(loginUser.getUsername()));
		this.authorities = list
				.stream()
				.map(role -> new SimpleGrantedAuthority(role))
				.toList();
	}
	
	// テンプレート内で ${#authentication.principal.loginUser} アクセスできるようにするため
	public LoginUser getLoginUser() {
		return loginUser;
	}
	
	// 各メソッドをオーバーライド
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO 自動生成されたメソッド・スタブ
		return authorities;
	}
# 

認証用サービスクラスの作成

UserDetailsServiceクラスをimplementsして作成
loadUserByUsernameメソッドをオーバーライドしてSQLを実行されるようにする
テーブルからユーザーを見つけたらフォームで送信したパスワードはフレームワーク側で認証してくれる

@RequiredArgsConstructor
@Service
public class AuthUserDetailsServiceImpl implements UserDetailsService {

    @Autowired 
    private LoginUserMapper loginUserMapper;

    // loadUserByUsername のオーバーライド UserDetails オブジェクトを返却する
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    	
        // マッパーまたはレポジトリ内でこの処理は定義しておく       
        LoginUser loginUser = loginUserMapper.findByUsername(username);
        
    	AuthUserDetails user = new AuthUserDetails(loginUser);
		return user;
    }
}

メソッドの最後でUserDetailsクラスをimplementsして作成したクラスのインスタンスを返却する
ユーザー情報を認証以降も利用する場合は、上記クラス内で持てるようにプロパティ、getter/setterを用意しておく

コントローラー、テンプレートの定義

  • コントローラー
    テンプレートを返すだけのシンプルな例
@Controller
public class SecurityController {

    @GetMapping("/login") 
    public String login() {
        return "login";
    }
}
  • ログイン用テンプレート 
<form th:action="@{/login}" method="post" name="Login_Form"
            class="form-signin">
    <h2 class="form-signin-heading">ログインしてください</h2>
    <hr class="colorgraph">
    <p th:if="${param.error}">ログインに失敗しました</p>
    <br>
    <input type="text" name="username" placeholder="username" value="hoge"/><br>
    <input type="password" name="password" placeholder="password" value="12345678"/> <br>
    <button name="Submit" value="Login" type="Submit">Login</button>
</form>
  • ログイン後のテンプレート
     
<body th:with="user=${#authentication.principal.loginUser}">
	<!-- 認証済みユーザー情報にアクセス -->

    <h1>ログイン成功</h1>
    <h2><span th:text="${user.username}"></span> さんHello!!!!</h2>

    <form th:action="@{/logout}" method="post">
    <button>Logout</button>
    </form>
</body>

まとめ

アノテーションをつけてクラスを定義するだけで Spring Security が全て自動的に認証してくれるのですごく楽です。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?