JAVA

jwt 토큰 인증 + Interceptor 검증.

girin_dev 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
반응형