[ShareMore] 6. Spring Security - 세션 로그인

CodeKong의 기술 블로그·2024년 1월 4일
1
post-thumbnail

사실 전 챕터에서 이번 챕터로 넘어오면서 많은 코드를 작성하였습니다.
DTO도 각 상황에 맞게 만들었고 서비스 코드나 예외 처리도 추가하였습니다.

따라하시는 분이 있다면 어렵지 않은 코드들이니 제 GitHub에 오셔서 코드 보시면 도움이 될 것 같습니다!

🚩 https://github.com/YASICJUNWOO/sharemore

🔅 이번 챕터에서는 Spring Security를 적용시키며 세션부터 JWT까지 가는 과정 중 기초적인 세션부터 구현해보려고 합니다!


✅ 먼저 의존성을 추가해주겠습니다

implementation 'org.springframework.boot:spring-boot-starter-security'

다음은 SecurityConfig을 구현해 주겠습니다.

@Configuration
public class SpringSecurityConfig {

    @Bean
    public SecurityFilterChain filerChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .cors().disable()
                .authorizeHttpRequests(request-> request
                        .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
                        .requestMatchers("/api/users/**","/status/**","/h2-console/**").permitAll()
                        .anyRequest().authenticated()
                )
                .headers(header -> header
                        .frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)
                ) //h2-console관련
                .formLogin(login -> login
                        .usernameParameter("email")
                        .passwordParameter("password")
                        .failureForwardUrl("/status/fail")
                        .defaultSuccessUrl("/status/ok",true)
                        .permitAll()
                )
                .logout(Customizer.withDefaults());

        return http.build();
    }
 }

spring security 3.0으로 넘어오면서 기존의 Adapter 방식이 변경되었습니다.

requestMatchers의 값들은 필요하신대로 추가/삭제 하시면되고
저는 id 값으로 email값을 사용해주었습니다.

로그인의 성공여부를 판단해 줄 controller를 작성해 주겠습니다.

@RestController
@RequestMapping("/status")
public class CheckController {

    @GetMapping("/ok")
    public String check() {
        return "ok";
    }

    @PostMapping("/fail")
    public ApiResponse<String> loginFail() {
        return ApiResponse.onFailure("401", "login fail", "fail");
    }

}

그다음 기존 Users 엔티티를 security에서 USER DB로 사용해 주기위해 UserDetail를 상속받도록 해주었습니다.

@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Builder
public class Users extends BaseEntity implements UserDetails {

	...

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getUsername() {
        return this.email;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }

로직 구현

이제 준비는 끝났고 로그인하는 로직에 관해 구현해보겠습니다.

먼저, 유저가 입력한 email을 통해 DB에서 해당 email을 가진 Users 객체를 가져올 수 있도록 UserDetailService를 상속받아줍니다.

@Component
@RequiredArgsConstructor
public class MyUserDetailService implements UserDetailsService {

    private final UserService userService;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        Users user = userService.getUserByEmail(email);
        return User.builder()
                .username(user.getEmail())
                .password(user.getPassword())
                .roles("USER")
                .build();
    }

}

다음은 입력한 비밀번호와 email을 통해 조회된 User의 비밀번호와 일치하는지 확인하는 코드를 구현해보겠습니다.

먼저 저는 따로 암호화 없이 raw password로 검증하게 했습니다.

public class SimplePaaswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence rawPassword) {
        return rawPassword.toString();
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(encode(rawPassword));
    }
}

이 encoder를 security config에 bean으로 등록해줍니다!

@Configuration
public class SpringSecurityConfig {

    ...
    
    @Bean
    PasswordEncoder passwordEncoder() {
        return new SimplePaaswordEncoder();
    }

}

성공!

0개의 댓글