관리 메뉴

샐님은 개발중

#3. 스프링 시큐리티를 활용한 로그인/회원가입 (2) 본문

포토폴리오/Spring-Boot,JPA - 쇼핑몰사이트 v2

#3. 스프링 시큐리티를 활용한 로그인/회원가입 (2)

샐님 2023. 8. 3. 03:07
728x90
반응형

1. 스프링 시큐리티를 이용해서 로그인 구현

- UserDetailsService  인터페이스

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

 

1) 기존에 만들었던 MemberService에 UserDetailsService 를 구현해보자.

(패키지/임포트 정보 생략)

@Service
@Transactional
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {

(중략)

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        Member member = memberRepository.findByEmail(email);
        if(member == null){
            throw new UsernameNotFoundException(email);
        }
        // user 객체를 반환
        return User.builder()
                .username(member.getEmail())
                .password(member.getPassword())
                .roles(member.getRole().toString())
                .build();
    }
}

2) 스프링 시큐리티 설정 (3.XX 대 버전, 2.xx 대 버전의 내용과 다릅니다.)

package won.shop.config;

import jakarta.servlet.DispatcherType;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
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.WebSecurityCustomizer;
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;
import won.shop.Service.MemberService;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Autowired
    MemberService memberService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().disable()
                .authorizeHttpRequests(request -> request
                        .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
                        .requestMatchers("/", "/members/new","/logout","/css/**","/*.ico","/img/**","/layout/**","/item/**").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(login -> {
                            try {
                                login
                                        .loginPage("/members/login")	//  로그인 페이지 지정
                                        .defaultSuccessUrl("/") // 로그인 성공 후 화면
                                        .usernameParameter("email")	//  submit할 아이디
                                        .failureUrl("/members/login/error")
                                        .and()
                                        .logout() // exception 필요
                                        .logoutRequestMatcher(new AntPathRequestMatcher("/members/logout")) // 로그아웃 url
                                        .logoutSuccessUrl("/"); // 로그아웃 후 이동할 화면
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }
                );

        return http.build();
    }





    // 비밀번호 암호화
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 해수함수를 이용하여 비밀번호를 암호화하여 저장.
        return new BCryptPasswordEncoder();

    }
}

* 리다이렉션이 너무 많습니다. 

원인 : url 마다 .permitAll()를 붙여줘야함.(SecurityConfig 수정 필요)

참고 사이트 : https://stackoverflow.com/questions/40495244/too-many-redirects-with-own-login-form-spring-security


http.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/")
.usernameParameter("email")
.failureUrl("/login/error").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll()
.logoutSuccessUrl("/");

3) LoginController.java 생성

- 로그인과 로그아웃 메소드를 생성합니다.

package won.shop.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequiredArgsConstructor
public class LoginController {

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

4) MainController.java 에서 로그인한 정보를 가져옵니다. 

package won.shop;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MainController {

    @GetMapping("/")
    public String main(@AuthenticationPrincipal User user, Model model){
        if(user != null){
            model.addAttribute("userName",user.getUsername());
        }

        return "/main";
    }
}

 - @AuthenticationPrincipal 을 이용해서 로그인한 정보를 model에 담아 화면에서 사용합니다. 

728x90
반응형