Spring 개념 정리(2021.11.26) - 2

Jonguk Kim·2021년 11월 26일
0

Spring

목록 보기
16/16

기본

<!-- .css 연결 (css 폴더의 style.css 파일 연결) -->
<link rel="stylesheet" type="text/css" href="/css/style.css">

<!-- .js 연결 (basic.js 파일 연결) -->
<script src="basic.js"></script>

스프링 시큐리티 프레임워크

  • 스프링 서버에 필요한 인증 및 인가를 위해 많은 기능을 제공함 (편의 제공)
  • '스프링 시큐리티' 프레임워크 추가 (build.gradle -> dependencies 추가 -> gradle 새로고침)
    // 스프링 시큐리티 추가
    implementation 'org.springframework.boot:spring-boot-starter-security'

1. 로그인/로그아웃 설정

  • '스프링 시큐리티' 활성화
@Configuration	   // 스프링(서버)이 기동되는 순간에 설정을 한다는 의미	
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override	// 부모 클래스의 기본설정을 변경하기 위해 사용
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 어떤 요청이든 '인증' => 예외 설정을 안하면 로그인 부터 해야함
                .anyRequest().authenticated()
                .and()	// 조건 추가
                    // 로그인 기능 허용
                    .formLogin()	
                    .defaultSuccessUrl("/")	// 성공하면 루트('/')로 이동
                    .permitAll()
                .and() 
                    // 로그아웃 기능 허용
                    .logout()
                    .permitAll();
    }
}
  • default 로그인 기능 ('서버주소/login')
    • Username: user
    • Password: spring 로그 확인 (서버 시작 시마다 변경됨)

2. 로그인 페이지 설정

  1. localhost:8080/ : 입력 요청
    -> .formLogin() : 어떤 요청이든 인증 받아야하므로 default 화면 리다이렉트
    -> .loginPage("/user/login"): 로그인 default 페이지 변경 (localhost:8080/user/login)
                .and()
                    // 로그인 기능 허용
                    .formLogin()
                    .loginPage("/user/login")			// 로그인 View	제공 (Get 요청 URL /user/login)
                    .defaultSuccessUrl("/")				// 로그인 처리 후 성공 시 URL
                    .failureUrl("/user/login?error")	// 로그인 처리 후 실패 시 URL
                    .permitAll()
  1. controller API 에서 설정한 view 페이지로 이동
    -> @GetMapping("/user/login") -> return "login" : (/templates/login.html)
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserController {

    // 회원 로그인 페이지
    @GetMapping("/user/login")
    public String login() {
        return "login";
    }

    // 회원 가입 페이지
    @GetMapping("/user/signup")
    public String signup() {
        return "signup";
    }
}

3. URL 허용 정책 설정

  • 개발자 도구 -> Network 에러 확인
  • 인증하기 전에 허용 설정
  • /CSS/** : 해당 폴더 뒤 모든 경로 허용
  // image 폴더를 login 없이 허용
  .antMatchers("/images/**").permitAll()
  // css 폴더를 login 없이 허용
  .antMatchers("/css/**").permitAll()

4. CSRF, FrameOptions 무시

  • 회원 관리 API 허용 (CSRF 무시)
  • get 요청 허용
.antMatchers("/user/**").permitAll()
  • post 요청 허용
http.csrf()
        .ignoringAntMatchers("/user/**");
  • h2-console 사용에 대한 허용 (CSRF, FrameOptions 무시)
http.csrf()
                .ignoringAntMatchers("/user/**");

5. 암호화

  • 복호화 불가능한 '일방향' 알고리즘
  1. 사용자가 로그인을 위해 "아이디, 패스워드 (평문)" 입력 → 서버에 로그인 요청
    1. 서버에서 패스워드 (평문) 암호화
      1. 평문 → (암호화 알고리즘) → 암호문
  2. DB 에 저장된 "아이디, 패스워드 (암호문)"과 일치 여부 확인
@Bean
public BCryptPasswordEncoder encodePassword() {
    return new BCryptPasswordEncoder();
}
  1. Service 영역에서 암호화
    public void registerUser(SignupRequestDto requestDto) {
        // 회원 ID 중복 확인
        String username = requestDto.getUsername();
        Optional<User> found = userRepository.findByUsername(username);
        if (found.isPresent()) {
            throw new IllegalArgumentException("중복된 사용자 ID 가 존재합니다.");
        }

        // 패스워드 암호화
        String password = passwordEncoder.encode(requestDto.getPassword());
        String email = requestDto.getEmail();

        // 사용자 ROLE 확인
        UserRoleEnum role = UserRoleEnum.USER;
        if (requestDto.isAdmin()) {
            if (!requestDto.getAdminToken().equals(ADMIN_TOKEN)) {
                throw new IllegalArgumentException("관리자 암호가 틀려 등록이 불가능합니다.");
            }
            role = UserRoleEnum.ADMIN;
        }

        User user = new User(username, password, email, role);
        userRepository.save(user);
    }

6. 로그인/로그아웃 기능

  • 스프링 시큐리티 사용 전
  • 스프링 시큐리티 사용 후
    • Client의 요청은 모두 Spring Security 를 거침
      • Spring Security 역할 (인증/인가)
        1. 성공 시: Controller 로 Client 요청 전달 (Client 요청 + 사용자 정보 (UserDetails))
        2. 실패 시: Controller 로 Client 요청 전달되지 않음 (Client 에게 Error Response 보냄)
  • 로그인 처리 과정
    1. Client
      1) 로그인 시도
      2) 로그인 시도할 username, password 정보를 HTTP body 로 전달 (POST 요청) (스프링 시큐리티 아이디를 username으로 사용, username과 password 프론트엔드한테 미리 말해야함)
      3) 로그인 시도 URL 은 WebSecurityConfig 클래스에서 변경 가능
      - 아래와 같이 설정 시 "POST /user/login" 로 설정됨 (.loginProcessingUrl("/user/login"))
@Override
            protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests()
                        .anyRequest().authenticated()
                        .and()
                            // [로그인 기능]
                            .formLogin()
                            // 로그인 처리 (POST /user/login)
                            .loginProcessingUrl("/user/login")
                            .permitAll();
}
  1. 인증 관리자 (Authentication Manager)
    1) UserDetailsService 에게 username 을 전달하고 회원상세 정보를 요청
  2. UserDetailsService
    1) 회원 DB 에서 회원 조회 (회원 정보가 존재하지 않을 시 → Error 발생)
    2) 조회된 회원 정보(user) 를 UserDetails 로 변환
    3) UserDetails 를 "인증 관리자"에게 전달
User user = userRepository.findByUsername(username)
                    .orElseThrow(() -> new UsernameNotFoundException("Can't find " + username));
                    
UserDetails userDetails = new UserDetailsImpl(user)                    
  1. "인증 관리자" 가 인증 처리
    1) 아래 2 개의 username, password 일치 여부 확인

    • Client 가 로그인 시도한 username, password
    • UserDetailsService 가 전달해준 UserDetails 의 username, password

    2) password 비교 시

    • Client 가 보낸 password 는 평문이고, UserDetails 의 password 는 암호문
    • Client 가 보낸 password 를 암호화해서 비교

    3) 인증 성공 시 → 세션에 로그인 정보 저장
    4) 인증 실패 시 → Error 발생

  • 로그아웃 처리 과정

    • "GET /user/logout" 요청 시 로그아웃 -> POST /user/logout 처리 필요
      • 이유: CSRF protection 이 기본적으로 enable 되어 있기 때문
      • CSRF protection 을 disable 하면 GET /user/logout 으로도 사용 가능
    • 서버 세션에 저장되어 있는 로그인 사용자 정보 삭제
  • 로그인된 회원 정보 사용 가능

    1. Spring Security 가 "로그인된 회원 정보"를 Controller 에게 전달해 줌
    2. Controller 에서 "로그인된 회원 정보 (UserDetailsImpl)" 사용하는 방법
@Controller
public class HomeController {
    @GetMapping("/")
    public String home(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        model.addAttribute("username", userDetails.getUsername());
        return "index";
    }
}

타임리프

  • 타임리프 모듈 추가 (build.gradle -> dependencies 추가 -> gradle 새로고침)
    // Thymeleaf (뷰 템플릿 엔진)
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
profile
개발일지

0개의 댓글