[Spring] 스프링 심화 - Spring Security 2

호호빵·2023년 1월 7일
0

Spring

목록 보기
21/24

Spring Security

  • 스프링 서버에 필요한 인증 및 인가를 위해 많은 기능을 제공해 줌으로써 개발의 수고를 덜어줌. -> 공식 문서
    • [Spring] 스프링 심화 - Spring Security 1
      - 적용하기
      - Spring Security의 요소 확인

    • [Spring] 스프링 심화 - Spring Security 2
      - Default Form Login 방식 사용
      - UserDetails, UserDetailsService custom
      - 비밀번호 암호화 이해 및 적용

    • [Spring] 스프링 심화 - Spring Security 3
      - CustomSecurityFilter 적용
      - @AuthenticationPrincipal
      - @Secured
      - ExceptionHandling - 401, 403

1. Default Form Login 방식 사용

  • 간단하게 한 줄로 처리
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig {

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        // h2-console 사용 및 resources 접근 허용 설정
        return (web) -> web.ignoring()
                .requestMatchers(PathRequest.toH2Console())
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());    
                // 아래 다섯줄이 위 한줄로
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CSRF 설정
        http.csrf().disable();

        http.authorizeRequests()
          //     .antMatchers("/h2-console/**").permitAll()
          //     .antMatchers("/css/**").permitAll()
          //     .antMatchers("/js/**").permitAll()
          //     .antMatchers("/images/**").permitAll()
          //     .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                .anyRequest().authenticated();

        // 로그인 사용
        http.formLogin();

        return http.build();
    }

}

2. UserDetails, UserDetailsSerivce 커스텀

  • 구현으로 custom
# UserDetailsImpl

public class UserDetailsImpl implements UserDetails {

    private final User user;   // 인증이 완료된 User 객체와 ID, PW
    private final String username;
    private final String password;

    public UserDetailsImpl(User user, String username, String password) {
        this.user = user;
        this.username = username;
        this.password = password;
    }

    public User getUser() {
        return user;
    }

    @Override  // 사용자의 권한을 GrantedAuthority로 추상화 및 반환
    public Collection<? extends GrantedAuthority> getAuthorities() {
        UserRoleEnum role = user.getRole();
        String authority = role.getAuthority();  // {"ROLE_USER"}

        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(authority);
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(simpleGrantedAuthority);

        return authorities;
    }

    @Override
    public String getUsername() {return this.username;}
	
    ...
    
}
# UserDetailsServiceImpl

@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다."));

        return new UserDetailsImpl(user, user.getUsername(), user.getPassword());
    }
}
// UserDetailsImpl 인증객체 생성에 사용

UserDetails userDetails = userDetailsService.loadUserByUsername(username)
Authentication auth = new UsernamePasswordAuthenticationToken
					  (userDetails, userDetails.getPassword(), userDetails.getAuthorities());

3. 비밀번호 암호화 이해 및 적용

  • Spring Security 가 제공하는 적응형 단방향 함수인 bCrypt를 사용하여 비밀번호를 암호화
    • 적응형 단방향 함수는 내부적으로 리소스의 낭비가 매우 심하기 때문에 API 요청 마다 사용자의 이름과 비밀번호를 검증하면 애플리케이션 성능이 크게 떨어질 수 있다. 따라서 세션, 토큰 과 같은 인증방식을 사용하여 검증하는 것이 속도 및 보안 측면에 유리하다.

    • 사용자 검증 흐름
    1. 사용자는 회원가입을 진행한다.
    2. 사용자의 정보를 저장할 때 비밀번호를 암호화하여 저장한다.
    3. 사용자는 로그인을 진행한다.
    4. 사용자가 입력한 정보를 통해 저장된 암호화된 비밀번호를 가져와 사용자가 입력한 암호와 비교한다.
    5. 사용자 인증이 성공하면 사용자의 정보를 사용하여 JWT 토큰을 생성하여 Header에 추가하여 반환하고 Client 는 이를 쿠키저장소에 저장한다.
    6. 사용자는 게시글 작성과 같은 요청을 진행할 때 발급받은 JWT 토큰을 같이 보내고 서버는 이를 빠르게 인증 하고 사용자의 요청을 수행한다.

    • 양방향 / 단방향
      • 양방향 암호 알고리즘
        • 암호화: 평문 → (암호화 알고리즘) → 암호문
        • 복호화: 암호문 → (암호화 알고리즘) → 평문
      • 단방향 암호 알고리즘
        • 암호화: 평문 → (암호화 알고리즘) → 암호문
        • 복호화: 불가 (암호문 → (암호화 알고리즘) → 평문)

  • 암호화 기능 추가하는 방법
# WebSecurityConfig에 추가

@Bean // 비밀번호 암호화 기능 등록
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    
+ PasswordEncoder는 matches 함수 제공
private boolean matches(CharSequence rawPW, String encodedPW)
						// 사용자가 입력한 PW, 암호화되어 DB에 저장된 PW

+ 사용 예시
	if(!passwordEncoder.matches("사용자가 입력한 비밀번호", "저장된 비밀번호")) {
		throw new IllegalAccessError("비밀번호가 일치하지 않습니다.");
	 }


제네릭과 와일드카드

profile
하루에 한 개념씩

0개의 댓글