1. SecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((auth) -> auth //모든 경로에 대하여 권한을 부여->이후 수정 필요
/* .requestMatchers("/**").permitAll()*/
.anyRequest().authenticated())
.formLogin(formLogin -> formLogin //로그인 페이지 지정
.loginProcessingUrl("/login")
.usernameParameter("userId")
.failureHandler(((request, response, exception) -> {
response.sendError(401);
}))
.permitAll()) //로그인 페이지는 누구나 접근 가능
.logout(logout -> logout
.logoutUrl("/logout"))
.csrf(auth -> auth.disable()); //개발 환경에서 임시적으로 비활성화
return http.build();
}
}
authorizeHttpRequests((auth) -> auth.anyRequest().authenticated())
- 모든 request들은 인증이 되어야 한다.
/*requestMatchers("/**).permitAll()
- 슬래시 아래 모든 경로로의 접근을 허용한다.
formLogin(formLogin->formLogin...)
- 폼 로그인 방식을 사용
loginProcessingUrl("/login")
- 로그인 처리 url을 설정
usernameParameter("userId")
- 스프링 시큐리티에서는 기본적으로 id의 파라미터 명을 username으로 인식
- 하지만 Member 엔티티에서는 userId를 로그인 파라미터로 사용
- 따라서 해당 함수를 사용하여 파라미터 명을 설정한다.
.failureHandler(((request, response, exception) -> {
response.sendError(401);
}).permitAll()
- 로그인에 실패할 경우 401 에러를 전송한다.
- 로그인폼은 모든 접근을 허가한다(로그인을 하기 위해 로그인이 필요하다면 모순)
.logout(logout -> logout.logoutUrl("/logout"))
- 로그아웃 url 설정
.csrf(auth -> auth.disable())
- 이 기능이 활성화되어있을 경우 request에는 csrf 토큰이 반드시 필요함
*csrf(Cross Site Request Forgery): 사용자가 자신의 의도와 상관없이 공격자가 의도한 행위를 요청하게 하는 공격
- 스프링 시큐리티에서는 기본적으로 이 기능이 활성화되어 있음
- 개발 환경에서는 테스트의 편의를 위해 일시적으로 비활성화
- 추후 배포 시에는 이를 제거하여 보안 기능을 활성화해야 함.
2. CustomUserDetailsService
/**
* 스프링 시큐리티 인증 관련 서비스
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomUserDetailsService implements UserDetailsService {
private final MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
Member member = memberRepository.findByUserId(userId);
if (member != null) {
log.info("find member: {}", userId);
return new MemberDetails(member);
}
log.info("find no member: {}", userId);
return null;
}
}
- 로그인 요청이 들어올 경우 스프링 시큐리티는 UserDetailService.loadByUsername 함수를 이용하여 아이디에 해당 하는 유저 정보를 찾음
- 따라서 해당 인터페이스를 구현해야 함
- 해당 함수는 UserDetails를 반환해야 하므로 Member에 맞는 UserDetails 인터페이스를 구현해야 함.
3. MemberDetails
@RequiredArgsConstructor
public class MemberDetails implements UserDetails {
private final Member member;
//컬렉션에 해당 회원의 권한을 담아 반환
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collections = new ArrayList<>();
collections.add(()->{
return member.getTeacherOrStudent().name();
});
return collections;
}
@Override
public String getPassword() {
return member.getPassword();
}
@Override
public String getUsername() {
return member.getUserId();
}
@Override
public boolean isAccountNonExpired() {
return LocalDate.now().isBefore(member.getLastLoginDate().plusMonths(3));
}
@Override
public boolean isAccountNonLocked() {
return LocalDate.now().isBefore(member.getLastLoginDate().plusYears(1));
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return !member.isWithdraw();
}
}
- 스프링 시큐리티에서 해당 인터페이스(UserDetails)의 구현체를 반환하므로 이를 구현
- 아래 함수들은 모두 UserDetails의 함수
- 스프링 시큐리티는 이 정보들을 토대로 요청과 비교하여 권한 부여 여부를 판단
4. 테스트
//테스트를 위한 임시 추가
@PostConstruct
public void init() {
SignupBody signupBody = new SignupBody("nam", "theperz", "abcd1234",
LocalDate.of(1999, 8, 3), Gender.M, 4, "이쪽", "상",
"ckdgh7884@gmail.com", "01013243253", false, false);
memberService.signUp(signupBody);
}
- 테스트를 위해 임시 데이터를 추가(userId: theperz, password: abcd1234)
- 데이터를 넣고 아이디와 비밀번호를 입력 후 요청 전송
- formLogin 방식을 사용할 경우 x-www-form-urlencoded 방식만 사용가능
- 정상적으로 JSESSIONID를 받는 것을 확인할 수 있음
- 404에러는 페이지가 없어서 생긴 오류
참고) 컨트롤러 단에서의 로그인 구현은?
- "/login" 경로로 POST 요청이 오면 스프링 시큐리티 내부적으로 UsernamPasswordAuthenticationFilter가 동작
- 이때 AuthenticationProvider에 의해 CustomUserDetailsService의 loadUserByUsername을 호출하여 DB에 있는 유저를 조회
- 위의 과정은 직접 커스텀하지 않아도 내부적으로 자동 등록되어 동작하기 때문에 우리가 직접 구현할 필요는 없다
'Proj > webClass' 카테고리의 다른 글
고객센터 Q&A crud 구현 (0) | 2024.04.10 |
---|---|
회원가입 개발 및 간단한 리팩토링 (0) | 2024.03.28 |
Hello Spring Security (1) | 2024.03.27 |