주특기 심화 주차부터는 Spring Security를 새로 배우고, 카카오 로그인을 구현해보았다.
공부하면서 나름대로 나 혼자만의 생각으로 정리한 내용들에 대해서 회고해보고자 한다.
Spring Security 프레임워크
스프링 서버에 필요한 인증 및 인가 기능을 대신 제공해줌으로써, 인증 / 인가 기능을 개발하기 위한 수고를 덜어준다.
@EnableWebSecurity
스프링 Security 지원을 가능하게 하는 어노테이션이다.
해당 어노테이션이 달린 클래스에서 Spring Security 동작 전반에 대한 설정을 진행할 수 있다.
여러 설정들
permitAll() - 인증 처리
http.authorizeRequests().antMatchers("/api/user/**").permitAll()
// 해당 URI의 요청들은 모두 인증처리 되어 컨트롤러로 전달
.anyRequest().authenticated();
// 그 외의 요청들은 모두 Authentication 인증을 하겠다.
formLogin() - 로그인 페이지 설정
http.formLogin();
//Security에서 제공하는 디폴트 Form Login을 사용하겠다는 표시.
http.formLogin().loginPage("/api/user/login-page").permitAll();
//내가 지정한 페이지를 로그인 페이지로 사용.
addFilterBefore() - 커스텀 필터 등록하기
http.addFilterBefore(new CustomSecurityFilter(userDetailsService,
passwordEncoder()), UsernamePasswordAuthenticationFilter.class);
UsernamePasswordAuthenticationFilter 이전에 CustomSecurityFilter를 두어 추가 검증하도록 한다.
exceptionHandling().accessDeniedPage() - 접근 제한 페이지 이동 설정
http.exceptionHandling().accessDeniedPage("/api/user/forbidden");
권한이 없는 유저가 페이지 접근 시, 접근 제한 알림 페이지로 이동하도록 설정한다.
이외에도 여러가지 설정들과 필터가 존재하지만, 우선적으로 내가 구현할 로그인 기능에 필요한 UsernamePasswordAutenticationFilter에 대해서만 알아두고자 한다.
UsernamePasswordAuthenticationFilter
기본적으로 Form Login 기반을 사용할 때, username 과 password를 확인하여 인증한다.
Form Login 기반은 인증이 필요한 URL 요청이 들어왔을 때 인증이 되지 않았다면 로그인 페이지를 반환한다.
로그인을 진행하면, 하기 이미지와 같은 유저정보가 담긴 인증객체가 반환되는데, 이 인증객체를 통해 인증을 진행하게 된다.
인증 객체는 SecurityContext로 접근할 수 있고, SecurityContext는 SecurityContextHolder로 접근할 수 있다.
// 예시코드
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
인증 객체 내부의 정보는 다음과 같다.
principal : 사용자를 식별한다. Username/Password 방식으로 인증할 때 보통 UserDetails 인스턴스다.
credentials : 주로 비밀번호, 대부분 사용자 인증에 사용하고 다음 비운다.
authorities : 사용자에게 부여한 권한을 GrantedAuthority 로 추상화하여 사용한다.
유저 권한은 회원가입 시 지정해준다.
UserDetailsService
해당 클래스에서는 UserDetailsService는 username/password 인증방식을 사용할 때 사용자를 조회하고 검증한 후 UserDetails를 반환한다. Custom하여 Bean으로 등록 후 사용 가능하다.
UserDetails
해당 클래스에서 유저의 정보, 크리덴셜, 권한을 하나로 묶는다.
검증된 UserDetails는 UsernamePasswordAuthenticationToken 타입의 Authentication를 만들 때 사용되며 해당 인증객체는 SecurityContextHolder에 세팅된다. Custom하여 사용가능하다.
정리해본 Spring Security 실행 과정은 다음과 같다.
Spring Security 실행 과정
1. HTTP 요청을 받으면, URI를 확인하고 인증되지 않은 URI일 경우 지정된 로그인 페이지로 이동시킨다.
2. 로그인을 진행하면 Filter에서 Username/ password 를 검증한다.
3. 로그인이 완료되면 UserDetailsService에서 일치하는 회원을 DB에서 검색한다.
4. 회원 정보로 UserDetails를 생성하여 Filter로 반환한다.
5. Filter에서 UserDetails을 활용하여 인증객체를 생성한다.
// 예시코드
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
6. 다음 필터로 이동한다.
filterChain.doFilter(request,response);
7. UsernamePasswordAuthenticationFilter 에서 인증객체를 확인하고, 최종적으로 HTTP 요청을 컨트롤러에 전달한다.
위의 과정을 이용해서, Jwt를 활용한 로그인도 구현할 수 있다.
로그인 시, 유저 정보를 담은 Jwt 토큰을 생성하고 로그인이 필요한 요청이 들어올 시, Custom 필터에 토큰을 검증하는 과정을 추가하면 된다.
이후의 과정은 Filter가 인증객체를 생성하고, UsernamePasswordAuthenticationFilter 가 인증객체를 검사하는 과정으로 이어진다.
비밀번호 암호화 및 인증
법적으로도 DB에는 사용자가 입력한 패스워드가 암호화되어 들어가야만 한다.
요즘에는 복호화가 불가능한 단방향 암호 알고리즘만을 사용한다고 한다.
Spring Security에는 패스워드를 암호하는 함수를 제공하며, 사용자가 입력한 비밀번호를 비교해 일치여부를 확인해주는 함수도 제공한다.
사용 예시
Congfig 파일에 암호화 기능을 등록
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
비밀번호 암호화
String password = passwordEncoder.encode(signupRequestDto.getPassword());
//비밀번호를 암호화해서 반환
패스워드 일치여부 확인
// 비밀번호 확인
if(!passwordEncoder.matches(password, user.getPassword())){
throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
}
느낀 점
Spring Security에 사용할 수 있는 Filter도 많고 정확한 원리를 이해하기에는 시간이 오래 걸릴 것 같았다. 일단 로그인 기능 구현에 필요한 것들을 중점적으로 공부하고자 했고, 단계별로 어떻게 흐름이 흘러가는지 큰 그림을 그려보고자 했다.
결과적으로 어느 정도는 흐름을 이해할 수 있게된 것 같다.
카카오 소셜 로그인도 구현해보았는데, 생각보다는 복잡하지 않게 느껴졌다.
나중에 구글, 네이버 로그인도 구현해보고 소셜 로그인에 대한 정리글을 따로 작성해보려고 한다.
새로운 프레임워크를 접할 때 마다 드는 생각은, 소스 코드 없이 이것을 사용할 수 있을까 하는 걱정이다. 기능 별로 흐름을 정리해두었지만 막상 실습 때 소스코드 없이 구현하려고 하면 막막하게 느껴지는 것 같다. 아예 안보고 사용하기는 내 수준에서는 어려울 것 같고, 최대한 많이 사용해보면서 기능에 익숙해지도록 노력해야겠다.
'항해99' 카테고리의 다른 글
항해 99 - 2023.02.25 TIL (0) | 2023.02.25 |
---|---|
항해 99 - 2023.02.23 TIL (0) | 2023.02.23 |
항해 99 - 2023.02.16 TIL (0) | 2023.02.16 |
항해 99 - 2023.02.15 TIL (0) | 2023.02.16 |
항해 99 - 2023.02.09 TIL (0) | 2023.02.10 |