[Spring Boot] 쇼핑몰 프로젝트 (6) - 스프링 시큐리티를 이용한 로그인/로그아웃

YulHee Kim·2021년 11월 27일
1

✏️ UserDetailsService

  • UserDetailService 인터페이스는 데이터베이스에서 회원 정보를 가져오는 역할을 담당합니다.
  • loadUserByUsername() 메소드가 존재하며, 회원 정보를 조회하여 사용자의 정보와 권한을 갖는 UserDetails 인터페이스를 반환합니다.

✏️ UserDetail

스프링 시큐리티에서 회원의 정보를 담기 위해서 사용하는 인터페이스는 UserDetails입니다. 이 인터페이스를 직접 구현하거나 스프링 시큐리티에서 제공하는 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();
    }
  • MemberService가 UserDetailsService를 구현합니다.
  • UserDetailsService 인터페이스의 loadUserByUsername() 메소드를 오버라이딩합니다. 로그인할 유저의 Email을 파라미터로 받습니다.

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에서 인증은 AuthenticationManager를 통해 이루어지며 AuthenticationManagerBuilder가 AuthenticationManager를 생성합니다. userDetailService를 구현하고 있는 객체로 memberService를 지정해줍니다.

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 = "test@email.com";
        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 = "test@email.com";
        String password = "1234";
        this.createMember(email, password);
        mockMvc.perform(formLogin().userParameter("email")
                        .loginProcessingUrl("/members/login")
                        .user(email).password("12345"))
                .andExpect(SecurityMockMvcResultMatchers.unauthenticated());
    }

}

✏️ Spring Security 태그

<sec:authorize="isAnonymous()">
 ...
<sec:authorize="isAuthenticated()">
 ...
<sec:authorize="hasAnyAuthority('ROLE_ADMIN')">
표현식설명
hasRole('role')해당 권한이 있을 경우
hasAnyRole('role1,'role2')포함된 권한 중 하나라도 있을 경우
isAuthenticated()권한에 관계없이 로그인 인증을 받은 경우
isFullyAuthenticated()권한에 관계없이 인증에 성공했고, 자동 로그인이 비활성인 경우
isAnonymous()권한이 없는 익명의 사용자일 경우
isRememberMe()자동 로그인을 사용하는 경우
permitAll모든 경우 출력함
denyAll모든 경우 출력하지 않음

참고 : https://codevang.tistory.com/272

profile
백엔드 개발자

0개의 댓글