JAVA

jwt 토큰 인증 + Interceptor 검증.

2022. 5. 22. 22:22
728x90
반응형

ID / PW 를 사용하는데 pw를 토큰으로 써보자.

 

* 이방식은 refresh token으로 만료 기한을 연장하는 방식이 아니며, 

access token의 만료기한만 검사.

 

 

목적 : interceptor 에서 사용자의 요청에 앞서 json token의 만료 기한을 검증한다. 

 

 

 

pom.xml에 의존성 추가. 


<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

 

 

토큰 생성.

    public static String createToken(UserDto user, String SECRET_KEY) {      
        //Header 설정
        Map<String, Object> headers = new HashMap<>();
        headers.put("typ", "JWT");
        headers.put("alg", "HS256");
        
        // algorithm
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        
        // secret key 
        Key key = new SecretKeySpec(SECRET_KEY.getBytes(), signatureAlgorithm.getJcaName());
        
        // 12 시간으로 유효시간 지정.
        long expiredTime = 1000 * 60L * 60L * 12L;
        
        return Jwts.builder()
        			.setHeader(headers)
        			.claim("user_id", user.getUser_id())
        			.claim("user_nm", user.getUser_nm())
					.setExpiration(new Date(System.currentTimeMillis() + expiredTime))
					.signWith(signatureAlgorithm, key)
					.compact();
    }

 

 

 

 

 

 

토큰 검증 : 

 

     만료 시간 만을 검증한다면, 

  //토큰 검증
   	public static boolean validateToken(String jwt, String SECRET_KEY) {
   		boolean result = false;
   		
   		try {
   			Claims claims = Jwts.parser()
			    				.setSigningKey(SECRET_KEY.getBytes())
			    				.parseClaimsJws(jwt)
			    				.getBody();
   			
   			result = !claims.getExpiration().before(new Date());
   		} catch (ExpiredJwtException e) {
   			throw e;
   		} catch (Exception e) {
   			return result;
   		}
   		
   		return result;
   	}

 

 

 

interceptor 설정. 

 

servlet-context.xml

 

  <interceptors>
        <interceptor>
			<mapping path="/**"/>
            <!-- 아래 특정 url 매핑 모두 인터셉터 대상에서 제외. -->
			<exclude-mapping path="/v2/api-docs"/>
			<exclude-mapping path="/swagger-resources/**"/>
			<exclude-mapping path="/swagger-ui.html"/>
			<exclude-mapping path="/webjars/**"/>
			<exclude-mapping path="/login/**"/>
			
            <exclude-mapping path="/task/**"/>
			            
			<beans:bean id="Interceptor" class="com.tester.tet.login.JwtInterceptor"/>	
        </interceptor>
    </interceptors>

 

 

interceptor class : 

 

import java.io.PrintWriter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.net.HttpHeaders;

import io.jsonwebtoken.ExpiredJwtException;


public class JwtInterceptor implements HandlerInterceptor {
	
    @Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		String jwt = request.getHeader(HttpHeaders.AUTHORIZATION);
		ObjectMapper objectMapper = new ObjectMapper();
		String jsonString = "";
		
		response.setContentType("application/json");
		response.setCharacterEncoding("UTF-8");
		
		if(!StringUtils.isEmpty(jwt)) {			
			try {						
				if(!JwtUtil.validateToken(jwt, SECRET_KEY)) {
					PrintWriter out = response.getWriter();
					
					response.setStatus(400);
	            	jsonString = objectMapper.writeValueAsString(new LoginResponseDto(HttpStatus.BAD_REQUEST.value(), "유효하지 않은 토큰", jwt));
					out.print(jsonString);
					out.flush();
	                return false;	      
	            }
			} catch (ExpiredJwtException e) {
				PrintWriter out = response.getWriter();			
				
				response.setStatus(400);
            	jsonString = objectMapper.writeValueAsString(new LoginResponseDto(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value(), "토큰 만료 기간 경과", jwt));
				out.print(jsonString);
				out.flush();
                return false;
			}			
		} else {
			PrintWriter out = response.getWriter();
			
			response.setStatus(400);
			jsonString = objectMapper.writeValueAsString(new LoginResponseDto(HttpStatus.BAD_REQUEST.value(), "토큰 없음", null));
			out.print(jsonString);
			out.flush();
            return false;
		}
		
		return true;
	}
    
    
    
    @Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
			String jwt = request.getHeader(HttpHeaders.AUTHORIZATION);
			
			HandlerMethod method = ((HandlerMethod)handler);
			
			String user_id = JwtUtil.getSubject(jwt, SECRET_KEY)
									.get("user_id")
									.toString();
			String access_ip = Utils.getUserIp();
			String action = request.getMethod();
			String service = method.getMethod().getName();
			
			userManageDAO.postUserHistory(user_id, access_ip, action, service);		
	}

}

 

 

 

사용자의 토큰을 API 단에서 쓰려고 한다면, 

 

@ApiOperation(value = "task request", notes = "작업 요청(등록)")
	@RequestMapping(value = "", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<TaskResponseDto> postTask(
			@RequestBody TaskRequestDto tsk_rqst_dto
			,HttpServletRequest request ) {

		try {

			String jwt = request.getHeader(HttpHeaders.AUTHORIZATION);
			String user_id = (String) JwtUtil.getSubject(jwt,SECRET_KEY).get("user_id");

			// 토큰에서 가져온 user_id를 사용한다.
			tsk_rqst_dto.setCreator_id(user_id);

			TaskResponseDto taskResponseDto = new TaskResponseDto();
			taskResponseDto = taskService.taskValidation(tsk_rqst_dto);

			if ( !taskResponseDto.getCode().equals("200")) {
				return new ResponseEntity<TaskResponseDto>(taskResponseDto,HttpStatus.BAD_REQUEST);
			}

			int task_id = taskService.postTask(tsk_rqst_dto);

			taskService.postTaskLog(task_id, tsk_rqst_dto.toString());

			userManageService.postUserHistory(user_id, request, "POST","postTask");

		} catch (Exception e) {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			PrintStream pinrtStream = new PrintStream(out);
			e.printStackTrace(pinrtStream);
			log_error.error(out.toString());
			return new ResponseEntity<TaskResponseDto>(new TaskResponseDto("500","Internal Server Error",null),HttpStatus.INTERNAL_SERVER_ERROR);
		}

		return new ResponseEntity<TaskResponseDto>(new TaskResponseDto("200","OK",null),HttpStatus.OK);
	}

 

 

 

 

320x100
반응형
저작자표시 (새창열림)

'JAVA' 카테고리의 다른 글

java directory copy / progress bar  (0) 2022.06.11
springboot / querydsl 설정  (0) 2022.05.31
java 정규 표현식  (0) 2022.05.09
java Stream  (0) 2022.03.08
Stack  (0) 2022.02.23
'JAVA' 카테고리의 다른 글
  • java directory copy / progress bar
  • springboot / querydsl 설정
  • java 정규 표현식
  • java Stream
girin_dev
girin_dev
기록합시다.
250x250
girin_dev
girin_dev
girin_dev

github.com/jaemanc


전체
오늘
어제
  • 분류 전체보기 (122)
    • ALGORITHM (23)
    • AWS (4)
    • Effective Java (4)
    • ERROR (12)
    • DB (11)
    • JAVA (23)
      • SPRING (10)
    • PYTHON (5)
      • TOY_PROJECT (1)
    • MOBILE (4)
    • SERVER (8)
    • TIPS (16)
    • WAS (2)
    • 새싹 일기 (5)
    • DATA (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • IntelliJ
  • vertica
  • 프로그래머스
  • java
  • dp
  • querydsl
  • 바질 키우기
  • springboot
  • lis
  • error
  • 새싹
  • docker
  • centos7
  • offset
  • spring boot
  • react-native
  • 다이나믹 프로그래밍
  • Effective Java
  • python3
  • 가장 큰 수
  • oracle cloud
  • oracle
  • 바질 페스토
  • 바둑이
  • jwt
  • CentOS 8
  • JAVA 11
  • Flutter
  • 바질
  • Chat GPT

최근 댓글

최근 글

hELLO · Designed By 정상우.
girin_dev
jwt 토큰 인증 + Interceptor 검증.
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.