JSON 웹 토큰
서버에서 전자 서명된 토큰을 이용하면 인즈엥 따른 스케일 문제를 해결할 수 가 있습니다! 우리는 전자 서명된 토큰을 이용해 스케일 문제를 해결합니다.ㅎㅎ 그리고 이렇게 전자 서명된 토큰
uno-kim.tistory.com
구현을 위해 일단 JWT 관련 라이브러리를 디펜던시에 추가해야합니다.!
implementation 'org.springframework.boot:spring-boot-starter-security'
빌드.그래들에 추가해줍시다!
OncePerRequestFilter 라는 클래스를 상속해 필터를 생성해야합니다.
위 클래스는 한 요청당 한번만 실행됩니다. 따라서 한번만 인증하면 되는 우리에게 맞는 필터입니다!

위 클래스를 상속받는 JwtAuthenticationFilter를 구현해 봅시다!
우선 인증부분만 구현하고 유효시간 검사는 생략하겠습니다. 추후 추가할때 다시 포스팅하겠습니다.><
package com.unoSpringBoot.study.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private TokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
try {
// 요청에서 토큰 가져오기
String token = parseBearerToken(request);
log.info("필터를 가동하고 있습니다.");
// 토큰 검사히고. JWT 이므로 인가 서버에 요청하지 않고도 검증 가능
if (token != null && !token.equalsIgnoreCase("null")) {
// userId 가져오기 . 위조된 경우 예외 처리된다.
String userId = tokenProvider.validateAndGetUserId(token);
log.info("<권한> id : " + userId);
// 인증완료. SecurityContexHolder에 등록해야 인증된 사용자라고 생각한다.
AbstractAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(//
userId//
// 인증된 사용자의 정보, 문자열이 아니어도 아무것이나 넣을 수 있다.
// 보통 UserDetails라는 오브젝를 넣는다.
, null, AuthorityUtils.NO_AUTHORITIES);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authenticationToken);
SecurityContextHolder.setContext(securityContext);
}
} catch (Exception e) {
// TODO: handle exception
logger.error("정상적으로 실행 할 수가 없습니다.");
}
filterChain.doFilter(request, response);
}
private String parseBearerToken(HttpServletRequest request) {
// TODO Auto-generated method stub
// HTTP 요청의 헤더를 파싱해 Bearer 토큰을 리턴한다.
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) {
return bearerToken.substring(7);
}
return null;
}
}
스프링 시큐리티 설정
이제 스프링 시큐리티를 사용하므로 스프링 시큐리티에 JwtAuthenticationFilter를 사용하라고 알려주어야합니다.
Config패키지 아래에 WebSecurityConfig 클래스를 생성하고 , WebSecurityConfigurerAdapter를 상속하는 클래스를
구현해야합니다.
package com.unoSpringBoot.study.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.web.filter.CorsFilter;
import com.unoSpringBoot.study.security.JwtAuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
@EnableWebSecurity
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
// http시큐리티 빌더
http.cors()// webMvcConfig에서 이미 설정했으므로 기본 cors 설정
.and().csrf().disable()// csrf는 현재 사용하지 않으므로 disable
.httpBasic().disable() // token을 사용하므로 basic인증 disalbe
.sessionManagement()// session 기반이 아님을 선언
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers("/", "/auth/**").permitAll() // '/'와 '/auth/**' 경로는 인증을 안해도됨 을선언
.anyRequest().authenticated(); // /와 /auth/**이외의 모든 경로는 인증해야함
/*
* filter등록 매 요청마다 CorsFilter 실행후에 jwtAuthenticationFilter를 실행한다.
*/
http.addFilterAfter(jwtAuthenticationFilter, CorsFilter.class);
}
}
HttpSecurity는 시큐리티 설정을 위한 오브젝트입니다.
이 오브젝트는 빌더를 제공하는데 빌더를 이용해
cors, csrf, httpBasic ,authorizeRequests 등 다양한 설정을 할 수 있다.
말하자면 우리는 web.xml 대신 HttpSecuriuty를 이용해 시큐리티 관련 설정을 하는 겁니다.
마지막 줄을 보면 addFilterAfter() 메서드를 실행하는 것을 알 수 있습니다.
이것도 실제로 실행하는 것이 아니라 JwtAuthenticationFilter를 CorsFilter 이후에 실행하라고 설정하는 겁니다.
CorsFilter 다음에 반드시 실행해야 하는 것은 아니지만 CorsFilter 다음이 적당한것같아 그렇게 설정했습니다.

간단히 테스팅을 해봅시다!
회원가입은 변한것이 없으므로 이전에 했던 것처럼 회원가입을 하고 로그인 요청을 해봅시다!
생성완료했구요!
로그인을 했습니다. 이때보면 토큰이 발급되어있는 것을 볼 수 있습니다.
"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI0MDI4ODA4YjdkYTlmZGE2MDE3ZGE5ZmVkMmIxMDAwMCIsImlzcyI6ImRlbW8gYXBwIiwiaWF0IjoxNjM5MjM0NjQ2LCJleHAiOjE2MzkzMjEwNDZ9.0B-tIbPyWRXI5M-66sxQ14A7lz4Ahclj42w1Nl_9Q6D0XSg8d_6K8LjjfbhvsWxsZGSaUto9NuIg-NLAsKll1Q",
토큰은 다음과 같습니다. 이제 로그인한 토큰을 복사하고 Get요청을 작성해봅시다.
포스트맨의 주소창 아래서 Authorization을 선택하고 바로 아래의 Type에서 BearerToken을 선택했습니다.
BearerToken 을 선택하면 오른쪽에 토큰을 넣을 수 있는 창이 나타나는데 여기 복사한 JWT를 넣습니다.
그리고 Send를 클릭했을때 error와 data가 뜨면 정상적으로 인증된것입니다!!
그럼 혹시 모든 요청을 인증하는 것이 아닐까...? 한다면 토큰 맨 마지막에 아무 문자열이나 넣고 다시 요청을 보내봅시다
토큰뒤에 아무거나 (위조된 토큰을 사용한 예시입니다.) 넣어주고 결과를 봤습니다!
403포비든이 리턴되는 것을 확인 할 수 있습니다.
다음에는 우리가 만든 Todo 컨트롤러는 모두 디폴트로 사용자 아이디를 "Temporary-user"를 사용하고 있었습니다.
이제 토큰에서 가져온 사용자 아이디로 Todo 컨트롤러에서 올바른 사용자를 지정해 주도록 하겠습니다.

'Spring boot 프로젝트 기록 > 3. 인증 백엔드 통합' 카테고리의 다른 글
패스워드 암호화 (0) | 2021.12.13 |
---|---|
TodoController에서 인증된 사용자 사용하기 (0) | 2021.12.12 |
스프링 시큐리티 통합 -1 (0) | 2021.12.08 |
REST security를 구현해보자! (0) | 2021.12.02 |
댓글