inblog logo
|
devleekangho
    스프링부트

    [스프링 부트] 27. 스프링 시큐리티(Spring Security)로 로그인 구현 - 웹 게시판 v5

    KangHo Lee's avatar
    KangHo Lee
    Nov 28, 2024
    [스프링 부트] 27. 스프링 시큐리티(Spring Security)로 로그인 구현  - 웹 게시판 v5
    Contents
    2. User Entity 수정3. UserService4. Request DTO에 비밀번호를 해시화 하는 코드 추가5. passwordEncoder(BCrypt) 테스트
    SecurityConfig
    @Configuration public class SecurityConfig { // 비밀번호 해시화 해주는 객체 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain configure(HttpSecurity http) throws Exception { // CSRF 보호 기능 비활성화 http.csrf(c -> c.disable()); // url pattern filter http.authorizeHttpRequests( r -> // 인증이 필요한 기능은 url에 /s/를 넣는다. r.requestMatchers("/s/**").authenticated().anyRequest().permitAll()) .formLogin(f -> f.loginPage("/login-form") // 인증 필요 시 "/login-form" 경로로 GET 요청 .loginProcessingUrl("/login") // 로그인 POST 요청 실행(loadUserByUsername 메서드) .defaultSuccessUrl("/")); // 로그인 완료되면 인덱스로 return http.build(); } }
    • http.csrf(c -> c.disable())
      • 시큐리티는 기본적으로 CSRF 공격 보호를 위해 CSRF 토큰 검증 기능이 있는데 지금은 연습을 위해 비활성화했습니다.
    • PasswordEncoder
      • 시큐리티는 해시화된 비밀번호만 취급하기 때문에 IoC 컨테이너에 비밀번호를 해시화 해주는 객체를 등록해야 합니다.
    • requestMatchers("/s/**")
      • 인증이 필요한 페이지의 url에 /s 를 추가합니다.
      • s 는 security 약자를 쓴 것으로 개발자가 임의로 정하면 됩니다.

    2. User Entity 수정

    User
    @AllArgsConstructor @NoArgsConstructor @Table(name = "user_tb") @Getter @Entity public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; @Column(nullable = false) private String email; @CreationTimestamp private Timestamp createdAt; // 권한 확인(role -> admin, manager, guest 같은) @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(); } // 계정이 만료되지 않았는지 확인합니다. @Override public boolean isAccountNonExpired() { return true; } // ID 잠금 여부를 확인합니다. @Override public boolean isAccountNonLocked() { return true; } // 비밀번호가 만료되지 않았는지 확인합니다. @Override public boolean isCredentialsNonExpired() { return true; } // 계정 비활성화 여부를 확인합니다. @Override public boolean isEnabled() { return true; } }
    • 시큐리티는 UserDetails 형태의 객체만을 받기 때문에 implements 해야 합니다.
    • getAuthorities()
      • 반환이 List인 이유는 권한이 여러 개 일수도 있기 때문입니다.
    • 유저 ID가 username이고 비밀번호가 password일 경우 Getter를 추가로 작성하지 않아도 됩니다.
    @Override public String getUsername() { return email; } @Override public String getPassword() { return password; }
    • username이 아니라 email을 검증하고 싶다면 getter를 오버라이드해야 합니다.

    3. UserService

    @RequiredArgsConstructor @Service public class UserService implements UserDetailsService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; @Transactional public void 회원가입(UserRequest.JoinDTO joinDTO) { userRepository.save(joinDTO.toEntity(passwordEncoder)); } // POST 요청 // /login 호출됨 // key 값이 정해져 있다 -> username, password // Content-Type -> x-www-form-urlencoded @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); return user; } }
    • loadUserByUsername 메서드
      • 설정
        • POST 방식 요청
        • URL은 “/login”
        • key의 이름은 username, password로 고정
        • Content-Type 은 application/x-www-form-urlencoded
      • 실행하는 작업
        • 사용자가 입력한 비밀번호와 DB의 비밀번호가 같은지 체크합니다.
        • 일치할 경우 세션에 username을 저장합니다.
    💡
    컨트롤러에 로그인 핸들러 메서드를 작성하지 않아도 됩니다.
    → LoginDTO도 필요 없습니다.

    4. Request DTO에 비밀번호를 해시화 하는 코드 추가

    UserRequest
    public class UserRequest { @Data public static class JoinDTO { private String username; private String password; private String email; public User toEntity(PasswordEncoder passwordEncoder) { // 비밀번호 해시화 String encPassword = passwordEncoder.encode(password); User user = new User(null, username, encPassword, email, null); return user; } } }
    • 시큐리티의 비밀번호 비교가 제대로 작동하려면 비밀번호를 해시화해서 저장해야 합니다.

    5. passwordEncoder(BCrypt) 테스트

    BCryptTest
    package com.metacoding.authblog._core; import org.junit.jupiter.api.Test; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class BCryptTest { @Test public void encode_test() { String password = "1234"; BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encodedPassword = encoder.encode(password); System.out.println(encodedPassword); } }
    • 여기서 해시화 된 비밀번호를 더미데이터로 넣어두면 로그인 테스트를 편하게 할 수 있습니다.
    data.sql
    insert into user_tb(username, password, email, created_at) values('ssar', '$2a$10$dx/JsPZ3FyiElsRA3p0h9OPkXTwBUZufKfokVSVTBGTdYZfhB8PQK', 'ssar@nate.com', now()); insert into user_tb(username, password, email, created_at) values('cos', '$2a$10$Tc9D9x1weKQZ/Y8nQnWnseTI6jhwb0IuPG5SNKHUXHP0IovpaZ2ie', 'cos@nate.com', now());
     
    Share article
    Contents
    2. User Entity 수정3. UserService4. Request DTO에 비밀번호를 해시화 하는 코드 추가5. passwordEncoder(BCrypt) 테스트

    devleekangho

    RSS·Powered by Inblog