SpringBoot]ショッピングモールアイテム(6)-Spring Securityを使用してログイン/ログアウト


✏️ UserDetailsService

  • ユーザー詳細サービスインタフェースは、データベースからメンバー情報を取得する責任を負います.
  • loadUserByUsername()メソッドが既に存在し、メンバー情報を表示することでユーザーの情報と許可されたUserDetailsインタフェースを返すことができます.
  • ✏️ UserDetail


    Spring Securityでは、メンバー情報を含むインタフェースがUserDetailsです.このインタフェースを直接実装するか、Spring Securityが提供するUserクラスを使用することができます.

    ログイン/ログアウトの実装


    MemberService.java

    @Override
        public UserDetails loadUserByUsername(String email) throws
                UsernameNotFoundException {
            Member member = memberRepository.findByEmail(email);
    
            if(member == null) {
                throw new UsernameNotFoundException(email);
            }
    
            return User.builder()
                    .username(member.getEmail())
                    .password(member.getPassword())
                    .roles(member.getRole().toString())
                    .build();
        }
  • MBERServiceは、ユーザー詳細サービスを実施します.
  • ユーザ詳細サービスインタフェースをカバーするloadUserByUsername()メソッド.ログインするユーザーの電子メールをパラメータとして受信します.
  • SecurityConfig.java

    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        MemberService memberService;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()
                    .loginPage("/members/login")
                    .defaultSuccessUrl("/")
                    .usernameParameter("email")
                    .failureUrl("/members/login/error")
                    .and()
                    .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/members/logout"))
                    .logoutSuccessUrl("/")
            ;
        }
    
    
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(memberService)
                    .passwordEncoder(passwordEncoder());
        }
    
    }
    Spring Securityでは、認証はアイデンティティマネージャによって行われ、アイデンティティマネージャジェネレータはアイデンティティマネージャを作成します.メンバーサービスをuserDetailServiceを実装しているオブジェクトとして指定します.

    MemberController.java

        @GetMapping(value = "/login")
        public String loginMember(){
            return "/member/memberLoginForm";
        }
    
        @GetMapping(value = "/login/error")
        public String loginError(Model model){
            model.addAttribute("loginErrorMsg", "아이디 또는 비밀번호를 확인해주세요");
            return "/member/memberLoginForm";
        }
    

    ログインのテスト


    MemberControllerTest.java

    @SpringBootTest
    @AutoConfigureMockMvc
    @Transactional
    @TestPropertySource(locations="classpath:application-test.yml")
    class MemberControllerTest {
    
        @Autowired
        private MemberService memberService;
    
        @Autowired
        private MockMvc mockMvc;
    
        @Autowired
        PasswordEncoder passwordEncoder;
    
        public Member createMember(String email, String password){
            MemberFormDto memberFormDto = new MemberFormDto();
            memberFormDto.setEmail(email);
            memberFormDto.setName("홍길동");
            memberFormDto.setAddress("서울시 마포구 합정동");
            memberFormDto.setPassword(password);
            Member member = Member.createMember(memberFormDto, passwordEncoder);
            return memberService.saveMember(member);
        }
    
        @Test
        @DisplayName("로그인 성공 테스트")
        public void loginSuccessTest() throws Exception{
            String email = "[email protected]";
            String password = "1234";
            this.createMember(email, password);
            mockMvc.perform(formLogin().userParameter("email")
                            .loginProcessingUrl("/members/login")
                            .user(email).password(password))
                    .andExpect(SecurityMockMvcResultMatchers.authenticated());
        }
    
        @Test
        @DisplayName("로그인 실패 테스트")
        public void loginFailTest() throws Exception{
            String email = "[email protected]";
            String password = "1234";
            this.createMember(email, password);
            mockMvc.perform(formLogin().userParameter("email")
                            .loginProcessingUrl("/members/login")
                            .user(email).password("12345"))
                    .andExpect(SecurityMockMvcResultMatchers.unauthenticated());
        }
    
    }

    #Springセキュリティラベル

    <sec:authorize="isAnonymous()">
     ...
    <sec:authorize="isAuthenticated()">
     ...
    <sec:authorize="hasAnyAuthority('ROLE_ADMIN')">
    式は、hasRole(「role」)がこの権限を持っている場合、isAuthenticated()権限を持っているかどうかにかかわらず、hasAnyRole(「role 1」>「role 2」)を含む権限が1つあれば、認証に成功したことを示します.自動ログインが非アクティブの場合、isAnonymous()権限を持たない匿名のユーザに対して、isRememberMe()を使用して自動ログインすると、permitAllはすべての場合denyAllを出力し、すべての場合出力しません.
    注意:https://codevang.tistory.com/272