Kakao.Auth.login
이용(프론트 팀원분께서 구현해주셨음!!)프론트에서 보내준 AccessToken으로 카카오 '사용자 정보 가져오기' API에 보낼 http 요청을 만든다
사용자 정보 가져오기 API 카카오 공식문서
WebSecurityConfig
andMatchers
에 /auth/login/kakao
등록해야 프론트에서 AccessToken
을 받을 수 있음
.antMatchers("/api/auth/kakao-login/**").permitAll()
이 정보를 우리 서버의 객체에 담는다.
/step1
으로 가야한다는 정보를 보낸다. (User 엔티티는 step2에서 받아온 정보로 만듦)/
(메인 페이지)로 가야 한다는 정보를 보낸다.로그인이 되어있다. = 1. access_token이 쿠키에 저장되어 있다 2. local storage에서도 user값이 저장되어있다
로그아웃이 됐다 = 1. cookie 삭제 2. local storage도 삭제
매 요청마다 스프링 시큐리티 필터(jwtAuthenticationFilter) 가 토큰을 검사한다.
우리 프로젝트에서는 CorsFilter를 실행한 후에(프론트가 3000번 포트에서 요청하니까) 토큰 필터(jwtAuthenticationFilter)가 실행되도록 설정하였다.
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
...
// cors 설정(코드 생략)
...
// filter 등록
// 매 요청마다
// CorsFilter 실행한 후에
// jwtAuthenticationFilter 실행한다
http.addFilterAfter(
jwtAuthenticationFilter,
CorsFilter.class
);
}
}
ublic class JwtAuthenticationFilter extends OncePerRequestFilter {
private final TokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
// 1.
String token = parseCookie(request);
log.info("Filter is running... token: {}",token);
if (token != null && !token.equalsIgnoreCase("null")) {
// 2. jwt이 위조된 경우 예외처리
String userEmail = tokenProvider.getEmailfromJwt(token);
log.info("Authenticated user ID : " + userEmail );
// 3.
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userEmail, // 인증된 사용자의 정보
null, //
AuthorityUtils.NO_AUTHORITIES
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//4.
SecurityContextHolder.getContext().setAuthentication(authentication); // 세션에서 계속 사용하기 위해 securityContext에 Authentication 등록
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
public String parseCookie(HttpServletRequest request){
String bearerToken = request.getHeader("Set-Cookie");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("access_token")) {
return bearerToken.substring(13);
}
return null;
}
request에서 쿠키 중 access_token의 정보를 가져온다. (JwtAuthenticationFilter 클래스의 parseCookie 함수)
토큰 -> 이메일로 디코딩해 사용자 이메일을 가져온다.
(디코딩하는 함수는 tokenProvider 클래스의 getEmailfromJwt 참고)
이때 userEmail
은 디코딩한 정보니까 인증된 정보이다!
💥 즉, 웹 브라우저에서 요청한 사용자가 userEmail을 가진 User엔티티의 주인임을 인증한 것!!!
인증된 이메일을 SecurityContextHolder
에 등록한다.
💥 요청에 쿠키가 없으면, 즉 로그인한 사용자가 아니면, 세션에는 아무것도 등록되지 않는다.
@AuthenticationPrincipal
을 붙인다.
@AuthenticationPrincipal
의 역할은 스프링이SecurityContextHolder
(4번)에서authentication
(3번, 인증된 이메일)을 가져와 컨트롤러의 userEmail에 넘겨주는 것이다.
예를 들어 게시판 생성 컨트롤러를 다음과 같이 짜면,
@Postmapping
public ResponseEntity<?> postBoard(@AuthenticationPrincipal String userEmail){
if(userEmail==null) {
return new ResponseEntity<>(new Message(ReturnCode.PLEASE_LOGIN, "/login"), HttpStatus.OK);
}
userRepository.findByEmail(userEmail);
userService.createBoard(userEmail);
...
}
userEmail
은 쿠키(로그인할 때 생성)로부터 얻은 인증된 이메일, 즉 웹 브라우저에서 요청한 사용자가 userEmail을 가진 User엔티티의 주인임을 인증한 것이라고 하였다
인증이 됐으므로, 이제부터 이 인증된userEmail
을 갖고 컨트롤러, 서비스, 레포지토리 단에서 userEmail에 해당하는 User 엔티티를 마음껏 쓰면 된다!!
로그인된 상태가 아니면, 쿠키가 없으니까 userEmail을 받지 못할테고, 그럼 로그인하라고 리턴하면 된다!
지금 코드에선 다 구현된 상태!! 5번 기능 쓰려면 컨트롤러에
@AuthenticationPrincipal
만 붙이면 된당~0~
React.js, 스프링 부트, AWS로 배우는 웹 개발 101 책을 참고+제 생각을 정리하여 작성하였습니다.