0
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.

SpringBoot SpringSecurity 認証、認可(単体テスト有り)

Last updated at Posted at 2022-07-25

概要

Spring Securityのプロダクトコードとテストコードを記事にします。
実装する機能は認証、認可です。

環境

OS:Windows10
IDE: IntelliJ community or CodeSpace
SpringBoot:2.7.0
Java:17
その他フレームワーク:SpringSecurity、JUnit5、SpringSecurityTest
github:https://github.com/RYA234/spring_boot_memo
ブランチは「Security」を選択。

画面

ログイン画面
image.png

ログイン成功後
image.png

ログアウト後
image.png

ログイン失敗時
image.png

プロダクトコード

SpringSecurityはクラスに@Configuration@EnableWebSecurityアノテーションを追加しています。

SecurityConfig.java
package com.example.spring_boot_memo.SpringSecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;


@EnableWebSecurity
@Configuration
public class SecurityConfig  extends WebSecurityConfigurerAdapter {



    @Autowired
    public PasswordEncoder passwordEncoder;

    /** セキュリティの各種設定 */
    @Override
    protected void configure(HttpSecurity http) throws Exception{

        // ログイン不要ページ
        http
                .authorizeRequests()
                .antMatchers("/index").permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/check/access").permitAll()
                .anyRequest().authenticated();
        // ログイン処理
        http
                .formLogin()
                .loginProcessingUrl("/login")
                .loginPage("/login")
                .failureUrl("/login?error")
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/menu/menu", true);
        http.csrf().disable();
    }
    // ログイン認証に使用するユーザー情報を設定
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth
                .inMemoryAuthentication()
                .withUser("User") 
                .password(passwordEncoder.encode("PASS"))
                .roles("GENERAL");
    }


}



パスワードの形式はBCryptを使っています。

BeanConfig.java
package com.example.spring_boot_memo.SpringSecurity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class BeanConfig {
    @Bean
    public PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}


テストコード

SecurityTest.java
package com.example.spring_boot_memo;

import static org.junit.Assert.assertEquals;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.security.NoSuchAlgorithmException;
import org.junit.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = SpringBootMemoApplication.class)
@AutoConfigureMockMvc
public class SecurityTest {
    @Autowired
    private MockMvc mvc;
    @Autowired
    PasswordEncoder passwordEncoder;

    @Test
    @DisplayName("認証成功後にアクセスページにログインアクセスできるか確認")
    @WithMockUser
    public void whenGetCustomers_thenStatus200() throws Exception {
        mvc.perform(get("/menu/menu"))
                .andExpect(status().isOk());

    }

    @Test
    @DisplayName("認証成功後にログアウトのエンドポイントへリクエストすると、/logoutへリダイレクトする")
    @WithMockUser
    public void Logout() throws Exception {
        mvc.perform(logout("/logout"))
        .andExpect(header().string("Location", "/login?logout"))
      .andExpect(redirectedUrlTemplate("/login?logout"))
        .andExpect(status().is3xxRedirection())
        ;

    }



    @Test
    @DisplayName("認証されてないリクエストのとき、loginページにリダイレクトすることを検証")
    public void AccessCheckInNoAuth() throws Exception {
        mvc.perform(get("/check/OK"))
                .andExpect(header().string("Location","http://localhost/login"));
        mvc.perform(get("/check/not_access"))
                .andExpect(header().string("Location","http://localhost/login"));
        mvc.perform(get("/menu/menu"))
                .andExpect(header().string("Location","http://localhost/login"));
        mvc.perform(get("/login"))
                .andExpect(status().isOk());

    }
    @Test
    @DisplayName("比較-エンコード後の生のパスワードとストレージからのエンコードされたパスワード")
    public void passwordBCryptMatchCheck() throws NoSuchAlgorithmException{
        String  expected = "PASS"; 
        String actual = passwordEncoder.encode(expected);
        Boolean isActual = passwordEncoder.matches(expected,actual);
        assertEquals(true, isActual);
    }
}


参考資料

公式ドキュメント

ByCryptのテストコードの書き方

リダイレクト先の書き方 Example8

プロダクトコードの書き方

0
0
0

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
0
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?