스프링 부트 쇼핑몰 프로젝트 with JPA (5)
스프링 시큐리티를 이용한 회원가입 및 로그인 -2
📌 로그인/로그아웃 구현하기
UserDetailsService 인터페이스
👉🏻 데이터베이스에서 회원 정보 가져옴.
👉🏻 localUserByUsername() 메소드 존재. 회원 정보 조회하여 사용자의 정보와 권한을 갖는 UserDetails 인터페이스 반환 
UserDetail 인터페이스
👉🏻 스프링 시큐리티에서 회원의 정보를 담기 위해 사용하는 인터페이스
👉🏻 직접 구현 OR 스프링 시큐리티에서 제공하는 User 클래스(UserDetails 인터페이스를 구현하고 있는 클래스) 사용 
기존의 MemberService가 UserDetailsService를 구현하게 하자.
package com.shop.service;
import com.shop.entity.Member;
import com.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
@RequiredArgsConstructor
public class MemberService implements UserDetailsService {
    private final MemberRepository memberRepository;
    public Member saveMember(Member member){
        validateDuplicateMember(member);
        return memberRepository.save(member);
    }
    private void validateDuplicateMember(Member member){
        Member findMember = memberRepository.findByEmail(member.getEmail());
        if(findMember != null) {
            throw new IllegalStateException("이미 가입된 회원입니다.");
        }
    }
    //로그인 할 User의 email을 파라미터로 전달받음.
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        Member member = memberRepository.findByEmail(email);
        if(member == null){
            throw new UsernameNotFoundException(email);
        }
        //UserDetail을 구현하는 User 객체를 반환.
        //User 객체를 생성하기 위해 회원의 정보를 넘겨 줌.
        return User.builder()
                .username(member.getName())
                .password(member.getPassword())
                .roles(member.getRole().toString())
                .build();
        }
}
👆🏻MemberService가 UserDetailsService를 구현함. loadUserByUsername메소드를 오버라이딩 하고, 로그인 할 User의 email을 파라미터로 전달받음. UserDetail을 구현하는 User 객체를 생성하고 반환함.
Securityconfig
package com.shop.config;
import com.shop.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecutiryConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    MemberService memberService;
    //http 요청에 대한 보안 설정 (페이지 권한, 로그인 페이지, 로그아웃 메서드 설정 작성)
    @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("/");
    }
    //BCryptPasswordEncoder() 해쉬함수 이용해서 비밀번호를 암호화
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    //spring Security에서 인증은 AuthenticationManagerBuilder 통해 이뤄짐
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(memberService)  //userDetail을 구현하는 객체로 memberService 지정
                .passwordEncoder(passwordEncoder());    //비밀번호 암호화
    }
}👆🏻configure 메소드에서 로그인 페이지 URL을 설정, 로그인 성공 시 이동할 URL 설정, 로그인 시 사용할 파라미터 이름으로 email을 지정, 로그인 실패시 이동할 URL 설정, 로그아웃 URL을 설정함.
👆🏻스프링 시큐리티에서 인증은 AuthenticationManagerBuilder가 생성한 AuthenticationManeger을 통해 함. 
Controller
package com.shop.controller;
import com.shop.dto.MemberFormDto;
import com.shop.entity.Member;
import com.shop.service.MemberService;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
@Controller
@RequestMapping("/members")
@RequiredArgsConstructor
public class MemberController {
....코드 생략.....
   @GetMapping("/login")
    public String loginMember(){
        return "member/memberLoginForm";
    }
    @GetMapping("/login/error")
    public String loginError(Model model){
        model.addAttribute("loginErrorMsg", "아이디 또는 비밀번호를 확인해주세요");
        return "/member/memberLoginForm";
    }
}
📌 실행화면
<로그인 화면>
<로그인 실패>

<로그인 성공>

📌 페이지 권한 설정하기
👉🏻 상품 등록 페이지는 ADMIN 계정만 가능하게 설정함.
ItemController
package com.shop.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ItemController {
    @GetMapping("/admin/item/new")
    public String itemForm(){
        return "/item/itemForm";
    }
}👆🏻상품 등록 페이지로 접근할 수 있도록 Controller에 설정함.
package com.shop.config;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}👆🏻AuthenticationEntryPoint 인터페이스를 통해 인증되지 않은 사용자가 리소스를 요청할 경우 에러가 발생하도록 함.
SecutiryConfig
@Override
    protected void configure(HttpSecurity http) throws Exception{
    
    .....코드 생략....
    
    http.authorizeRequests()
                .mvcMatchers("/", "/members/**",
                        "/item/**", "/images/**").permitAll()
                .mvcMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated();
        http.exceptionHandling()
                .authenticationEntryPoint(
                        new CustomAuthenticationEntryPoint());
    }
    
    @Override
    public void configure(WebSecurity web) throws Exception{
        web.ignoring().antMatchers("/css/**", "/js/**", "/img/**");
    }Author And Source
이 문제에 관하여(스프링 부트 쇼핑몰 프로젝트 with JPA (5)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@uuuuu_j_/스프링-부트-쇼핑몰-프로젝트-with-JPA-저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)