leeheejo 5 роки тому
батько
коміт
f1ea28ca8b

+ 88 - 41
java/com/icontrols/oauth/controller/OAuthController.java

@@ -10,7 +10,6 @@ import java.util.Base64.Encoder;
 import java.util.List;
 import java.util.Random;
 
-import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
 import org.slf4j.Logger;
@@ -43,6 +42,7 @@ import com.icontrols.oauth.model.WallpadCode;
 import com.icontrols.oauth.repo.ClientInfoRepository;
 import com.icontrols.oauth.repo.ComplexInfoRepository;
 import com.icontrols.oauth.utils.AES256Util;
+import com.icontrols.oauth.utils.DanziAuthUtils;
 import com.icontrols.oauth.utils.JWTUtils;
 
 @RestController
@@ -76,8 +76,14 @@ public class OAuthController {
 		logger.info("*1. response_type: " + responseType);
 		logger.info("*2. client_id: " + clientId);
 		logger.info("*3. state: " + state);
-		logger.info("*4. scope: " + scope);
+//		logger.info("*4. scope: " + scope);
 		logger.info("*5. redirect_uri: " + redirectUri);
+		// 월패드 인증코드 시간이 만료되어 history.go(-1)로 해당 페이지에 들어오는 경우에
+		// 세션에 저장되었던 사용자 정보를 지움.
+		if (httpSession.getAttribute("wallpadCode") != null)
+			httpSession.removeAttribute("wallpadCode");
+		if (httpSession.getAttribute("homeInfo") != null)
+			httpSession.removeAttribute("homeInfo");
 
 		ModelAndView mav = new ModelAndView();
 
@@ -92,11 +98,15 @@ public class OAuthController {
 			if (!responseType.equals("code")) {
 				throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.UNSUPPORTED_GRANT_TYPE);
 			} else {
-
+				// 클라이언트에 따라 endpoint를 변경해야해서 세션에 클라이언트 정보를 저장.
+				httpSession.setAttribute("clientId", clientId);
 				httpSession.setAttribute("redirectUri", redirectUri);
 				httpSession.setAttribute("state", state);
 
-				// TODO scope != null -> httpSession.setAttribute("scope", scope);
+				// TODO
+//				if (scope != null)
+//					httpSession.setAttribute("scope", scope);
+
 				List<ComplexInfo> complexInfos = complexInfoRepo.findAll();
 				model.addAttribute("complexInfos", complexInfos);
 
@@ -110,7 +120,7 @@ public class OAuthController {
 	}
 
 	@RequestMapping(value = "/user/info/submit", method = RequestMethod.GET)
-	public ModelAndView sendCode(HttpSession httpSession, HttpServletResponse httpResponse,
+	public ModelAndView sendCode(HttpSession httpSession,
 			@RequestParam(value = "complex", required = true) String complex,
 			@RequestParam(value = "dong", required = true) String dong,
 			@RequestParam(value = "ho", required = true) String ho)
@@ -126,7 +136,7 @@ public class OAuthController {
 		logger.info("*2. dong: " + dong);
 		logger.info("*3. ho: " + ho);
 
-		// TODO 단지서버로 인증번호 전송해야함.
+		String clientId = httpSession.getAttribute("clientId").toString();
 
 		// wallpad 코드 생성
 		Random generator = new Random();
@@ -134,22 +144,16 @@ public class OAuthController {
 		for (int i = 0; i < 6; i++) {
 			wallpadCode += Integer.toString(generator.nextInt(10));
 		}
+
 		logger.info("*[wallpadCode]");
 		logger.info("*code: " + wallpadCode);
 
 		// 세대정보 인코딩
-		String homeInfo = complex + "/" + dong + "/" + ho;
-		// db에 저장
-
-		ValueOperations<String, Object> vop = redisTemplate.opsForValue();
-		Duration d = Duration.ofSeconds(180);
+		String homeInfo = complex + "/" + dong + "/" + ho + "/" + clientId;
 
-		while (!vop.setIfAbsent(wallpadCode, homeInfo, d)) {
-			wallpadCode = "";
-			for (int i = 0; i < 6; i++) {
-				wallpadCode += Integer.toString(generator.nextInt(10));
-			}
-		}
+		// 2019.05.02 DB저장에서 세션 저장 방식으로 변경
+		httpSession.setAttribute("wallpadCode", wallpadCode);
+		httpSession.setAttribute("homeInfo", homeInfo);
 
 		logger.info("*[redis]");
 		logger.info("*(key,value): (" + wallpadCode + "," + homeInfo + ")");
@@ -157,7 +161,13 @@ public class OAuthController {
 		// 단지서버로 월패드인증번호 전송
 		WallpadCode body = new WallpadCode(dong, ho, wallpadCode);
 		logger.info(body.toString());
-		String url = "http://" + complex + ":8002/kakao/auth";
+
+		ClientInfo clientInfo = clientInfoRepo.findByClientId(clientId);
+		String endPoint = clientInfo.getEndPoint();
+
+//		String url = "http://" + complex + ":8002/kakao/auth";
+		String url = "http://" + complex + endPoint+"/auth";
+
 		logger.info(url);
 
 		// TODO 예외처리 단지통신
@@ -191,12 +201,25 @@ public class OAuthController {
 		logger.info("*[parameters]");
 		logger.info("*1. Input code: " + wallpadCode);
 
-		ValueOperations<String, Object> vop = redisTemplate.opsForValue();
-		if (vop.get(wallpadCode) == null) { // 없으면 null
-
+//		ValueOperations<String, Object> vop = redisTemplate.opsForValue();
+//		if (vop.get(wallpadCode) == null) { // 없으면 null
+//
+//			return false;
+//		}
+//		httpSession.setAttribute("homeId", vop.get(wallpadCode));
+
+		// 2019.05.02 DB저장에서 세션 저장 방식으로 변경
+		logger.info(httpSession.getAttributeNames().toString());
+		if (httpSession.getAttribute("wallpadCode") == null) {
+			logger.info("null");
 			return false;
+		} else {
+			if (!httpSession.getAttribute("wallpadCode").toString().equals(wallpadCode)) {
+				logger.info(httpSession.getAttribute("wallpadCode").toString());
+				return false;
+			}
 		}
-		httpSession.setAttribute("homeId", vop.get(wallpadCode));
+		logger.info(httpSession.getAttribute("wallpadCode").toString());
 		return true;
 	}
 
@@ -209,14 +232,14 @@ public class OAuthController {
 		logger.info("**************************************************************");
 		logger.info("**************************************************************");
 		logger.info("*[parameters]");
-		logger.info("*1. home id: " + httpSession.getAttribute("homeId"));
+		logger.info("*1. home id: " + httpSession.getAttribute("homeInfo"));
 
 		String code = "";
 		String homeInfo = "";
 		ValueOperations<String, Object> vop = redisTemplate.opsForValue();
 
 		// 코드 발급
-		homeInfo = httpSession.getAttribute("homeId").toString();
+		homeInfo = httpSession.getAttribute("homeInfo").toString();
 		logger.info("*[home info]");
 		logger.info("*1. homeInfo: " + homeInfo);
 		code = "";
@@ -281,38 +304,62 @@ public class OAuthController {
 				String complexIp = homeInfo.split("/")[0];
 				Encoder encoder = Base64.getEncoder();
 				complexIp = new String(encoder.encode(complexIp.getBytes()));
-				JWTInfo jwtinfo = new JWTInfo(complexIp, encodedHomeInfo);
+				JWTInfo jwtinfo = new JWTInfo(complexIp, encodedHomeInfo, homeInfo.split("/")[3]);
 				newAccessToken = JWTUtils.generateToken(jwtinfo, "accessToken");
 				newRefreshToken = JWTUtils.generateToken(jwtinfo, "refreshToken");
 
+				token.setAccess_token(newAccessToken);
+				token.setRefresh_token(newRefreshToken);
+
+				String clientId = JWTUtils.getClientInfoFromToken(newAccessToken);
+				ClientInfo clientInfo = clientInfoRepo.findByClientId(clientId);
+				String url = clientInfo.getEndPoint();
+				if (!DanziAuthUtils.create(token, url)) {
+					throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
+				}
+
 			}
 		} else if (grantType.equals("refresh_token")) {
 			if (refreshToken != null) {
-				Boolean bool = JWTUtils.validateToken(refreshToken, "refreshToken");
-				if (bool) {
-					// TODO token의 payload에서 compelxCd, homeId 가져오고 새로 토큰 생성한다
-					if (headers.get("Authorization").get(0) == null)
-						throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, ERROR.INVALID_CLIENT);
-
-					if (!validateHeaderAuth(headers.get("Authorization").get(0)))
-						throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, ERROR.INVALID_CLIENT);
-
-					JWTInfo jwtinfo = JWTUtils.getJWTInfoFromToken(refreshToken);
-					newAccessToken = JWTUtils.generateToken(jwtinfo, "accessToken");
-					newRefreshToken = JWTUtils.generateToken(jwtinfo, "refreshToken");
-				} else {
+				// 1. 정상적으로 생성되었는지 확인
+				if (!JWTUtils.validateToken(refreshToken, "refreshToken"))
 					throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.INVALID_REFRESHTOKEN);
-				}
+
+				// client정보검사
+				if (headers.get("Authorization").get(0) == null)
+					throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, ERROR.INVALID_CLIENT);
+
+				if (!validateHeaderAuth(headers.get("Authorization").get(0)))
+					throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, ERROR.INVALID_CLIENT);
+
+				// 단지에 갱신토큰 유효성 확인
+				String clientId = JWTUtils.getClientInfoFromToken(refreshToken);
+				ClientInfo clientInfo = clientInfoRepo.findByClientId(clientId);
+				String url = clientInfo.getEndPoint();
+
+				if (!DanziAuthUtils.get(url, refreshToken))
+					throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR); // 에러를 invalid ? internal?
+
+				JWTInfo jwtinfo = JWTUtils.getJWTInfoFromToken(refreshToken);
+				newAccessToken = JWTUtils.generateToken(jwtinfo, "accessToken");
+				newRefreshToken = JWTUtils.generateToken(jwtinfo, "refreshToken");
+				token.setAccess_token(newAccessToken);
+				token.setRefresh_token(newRefreshToken);
+
+				if (!DanziAuthUtils.refresh(url, token, refreshToken))
+					throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
+
 			}
 		} else {
 			throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
 		}
 
-		token.setAccess_token(newAccessToken);
-		token.setRefresh_token(newRefreshToken);
 		// class 만들어서 사용하는 것으로 변경해야함.
 		logger.info(token.toString());
 
+		// TODO 단지서버로 전송하고 정상 응답 받은 경우에만 정상리턴 -> 아닌경우에는 500
+		// 토큰 생성인 경우와 갱신인 경우 구분해서 처리 create, refresh
+
 		return token;
 
 	}

+ 15 - 8
java/com/icontrols/oauth/controller/ViewController.java

@@ -4,6 +4,7 @@ import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 import java.security.NoSuchAlgorithmException;
 import java.time.Duration;
+import java.util.Random;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,13 +32,19 @@ public class ViewController {
 	public String validTest(@RequestParam(value = "token", required = false) String token)
 			throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
 
-		JWTUtils jwt = new JWTUtils();
-		Boolean bool = jwt.validateToken(token, "accessToken");
-		logger.info("validate :" + bool.toString());
-		String cmpIp = jwt.getCmpIpFromToken(token);
-		logger.info("cmpIp :" + cmpIp);
-		String homeId = jwt.getHomeIdFromToken(token);
-		logger.info("homeId :" + homeId);
-		return bool.toString();
+		ValueOperations<String, Object> vop = redisTemplate.opsForValue();
+		Random generator = new Random();
+		String wallpadCode = "";
+		for (int i = 0; i < 6; i++) {
+			wallpadCode += Integer.toString(generator.nextInt(10));
+		}
+		while (!vop.setIfAbsent(wallpadCode, "value")) {
+			wallpadCode = "";
+			for (int i = 0; i < 6; i++) {
+				wallpadCode += Integer.toString(generator.nextInt(10));
+			}
+		}
+
+		return "";
 	}
 }

+ 20 - 1
java/com/icontrols/oauth/model/ClientInfo.java

@@ -20,6 +20,8 @@ public class ClientInfo {
 	private String clientCode;
 	@Column(name = "client_key")
 	private String clientSecret;
+	@Column(name = "end_point")
+	private String endPoint;
 
 	public String getClientId() {
 		return clientId;
@@ -53,6 +55,14 @@ public class ClientInfo {
 		this.clientSecret = clientSecret;
 	}
 
+	public String getEndPoint() {
+		return endPoint;
+	}
+
+	public void setEndPoint(String endPoint) {
+		this.endPoint = endPoint;
+	}
+
 	public ClientInfo(String clientId, String clientName, String clientCode, String clientSecret) {
 		super();
 		this.clientId = clientId;
@@ -61,6 +71,15 @@ public class ClientInfo {
 		this.clientSecret = clientSecret;
 	}
 
+	public ClientInfo(String clientId, String clientName, String clientCode, String clientSecret, String endPoint) {
+		super();
+		this.clientId = clientId;
+		this.clientName = clientName;
+		this.clientCode = clientCode;
+		this.clientSecret = clientSecret;
+		this.endPoint = endPoint;
+	}
+
 	public ClientInfo() {
 		super();
 	}
@@ -68,7 +87,7 @@ public class ClientInfo {
 	@Override
 	public String toString() {
 		return "ClientInfo [clientId=" + clientId + ", clientName=" + clientName + ", clientCode=" + clientCode
-				+ ", clientSecret=" + clientSecret + "]";
+				+ ", clientSecret=" + clientSecret + ", endPoint=" + endPoint + "]";
 	}
 
 }

+ 14 - 33
java/com/icontrols/oauth/model/JWTInfo.java

@@ -5,6 +5,7 @@ import java.util.Date;
 public class JWTInfo {
 	private String cmpIp;
 	private String homeId;
+	private String clientInfo;
 //	private Date exp;
 //	private Date iat;
 
@@ -12,18 +13,12 @@ public class JWTInfo {
 		super();
 	}
 
-//	public JWTInfo(String cmpIp, String homeId, Date exp, Date iat) {
-//		super();
-//		this.cmpIp = cmpIp;
-//		this.homeId = homeId;
-////		this.exp = exp;
-////		this.iat = iat;
-//	}
+	public String getClientInfo() {
+		return clientInfo;
+	}
 
-	public JWTInfo(String cmpIp, String homeId) {
-		super();
-		this.cmpIp = cmpIp;
-		this.homeId = homeId;
+	public void setClientInfo(String clientInfo) {
+		this.clientInfo = clientInfo;
 	}
 
 	public String getCmpIp() {
@@ -42,30 +37,16 @@ public class JWTInfo {
 		this.homeId = homeId;
 	}
 
+	public JWTInfo(String cmpIp, String homeId, String clientInfo) {
+		super();
+		this.cmpIp = cmpIp;
+		this.homeId = homeId;
+		this.clientInfo = clientInfo;
+	}
+
 	@Override
 	public String toString() {
-		return "JWTInfo [cmpIp=" + cmpIp + ", homeId=" + homeId + "]";
+		return "JWTInfo [cmpIp=" + cmpIp + ", homeId=" + homeId + ", clientInfo=" + clientInfo + "]";
 	}
 
-//	public Date getExp() {
-//		return exp;
-//	}
-//
-//	public void setExp(Date exp) {
-//		this.exp = exp;
-//	}
-//
-//	public Date getIat() {
-//		return iat;
-//	}
-//
-//	public void setIat(Date iat) {
-//		this.iat = iat;
-//	}
-//
-//	@Override
-//	public String toString() {
-//		return "JWTInfo [cmpIp=" + cmpIp + ", homeId=" + homeId + ", exp=" + exp + ", iat=" + iat + "]";
-//	}
-
 }

+ 137 - 0
java/com/icontrols/oauth/utils/DanziAuthUtils.java

@@ -0,0 +1,137 @@
+package com.icontrols.oauth.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.tomcat.util.json.JSONParser;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import com.icontrols.oauth.model.ClientInfo;
+import com.icontrols.oauth.model.Token;
+import com.icontrols.oauth.repo.ClientInfoRepository;
+
+public class DanziAuthUtils {
+	private static final Logger logger = LoggerFactory.getLogger(DanziAuthUtils.class);
+
+	@Autowired
+	ClientInfoRepository clientInfoRepo;
+
+	public static boolean create(Token newToken, String url)
+			throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
+
+		Map<String, Object> body = new HashMap<>();
+
+		Map<String, Object> token = new HashMap<>();
+		token.put("accessToken", newToken.getAccess_token());
+		token.put("refreshToken", newToken.getRefresh_token());
+
+		body.put("id", System.currentTimeMillis() + "");
+		body.put("type", "CREATE");
+		body.put("body", token);
+
+		logger.info(body.toString());
+
+		String complex = JWTUtils.getCmpIpFromToken(newToken.getAccess_token());
+		String endpoint = "http://" + complex + url + "/accessToken";
+
+		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+		factory.setReadTimeout(3000);
+		factory.setConnectTimeout(3000);
+		RestTemplate template = new RestTemplate(factory);
+		HttpEntity<Object> entity = new HttpEntity<Object>(body);
+		String answer = null;
+		try {
+			answer = template.postForObject(endpoint, entity, String.class);
+		} catch (Exception e) {
+			return false;
+		}
+
+		logger.info(answer);
+		JSONObject obj = new JSONObject(answer);
+		if (!obj.get("result").toString().equalsIgnoreCase("success")) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public static boolean refresh(String url, Token newToken, String prevToken) {
+
+		Map<String, Object> body = new HashMap<>();
+		Map<String, Object> token = new HashMap<>();
+		token.put("prevRefreshToken", prevToken);
+		token.put("newAccessToken", newToken.getAccess_token());
+		token.put("newRefreshToken", newToken.getRefresh_token());
+
+		body.put("id", System.currentTimeMillis() + "");
+		body.put("type", "REFRESH");
+		body.put("body", token);
+
+		String complex = JWTUtils.getCmpIpFromToken(prevToken);
+		String endpoint = "http://" + complex + url + "/accessToken";
+
+		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+		factory.setReadTimeout(3000);
+		factory.setConnectTimeout(3000);
+		RestTemplate template = new RestTemplate(factory);
+		HttpEntity<Object> entity = new HttpEntity<Object>(body);
+		String answer = null;
+		try {
+			answer = template.postForObject(endpoint, entity, String.class);
+		} catch (Exception e) {
+			return false;
+		}
+
+		logger.info(answer);
+		JSONObject obj = new JSONObject(answer);
+		if (!obj.get("result").toString().equalsIgnoreCase("success")) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public static boolean get(String url, String refreshToken) {
+
+		Map<String, Object> body = new HashMap<>();
+		Map<String, Object> token = new HashMap<>();
+		token.put("refreshToken", refreshToken);
+
+		body.put("id", System.currentTimeMillis() + "");
+		body.put("type", "GET");
+		body.put("body", token);
+
+		String complex = JWTUtils.getCmpIpFromToken(refreshToken);
+		String endpoint = "http://" + complex + url + "/accessToken";
+
+		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+		factory.setReadTimeout(3000);
+		factory.setConnectTimeout(3000);
+		RestTemplate template = new RestTemplate(factory);
+		HttpEntity<Object> entity = new HttpEntity<Object>(body);
+		String answer = null;
+		try {
+			answer = template.postForObject(endpoint, entity, String.class);
+		} catch (Exception e) {
+			return false;
+		}
+
+		logger.info(answer);
+		JSONObject obj = new JSONObject(answer);
+		if (!obj.get("result").toString().equalsIgnoreCase("success")) {
+			return false;
+		}
+
+		return true;
+	}
+
+}

+ 34 - 9
java/com/icontrols/oauth/utils/JWTUtils.java

@@ -35,15 +35,15 @@ public class JWTUtils {
 		String jwt = "";
 		if (type.equals("accessToken")) {
 			secretKey = CONSTANTS.ACCESS_TOEKN_SECRET_KEY;
-			jwt = Jwts.builder().setExpiration(new Date(3600000)) // 1시간 -> 설정정보로 변경해야함
+			jwt = Jwts.builder().setExpiration(new Date(43200000)) // 1시간 -> 설정정보로 변경해야함
 					.claim("cmpIp", info.getCmpIp()).claim("homeId", info.getHomeId())
-					.claim("iat", System.currentTimeMillis() / 1000)
+					.claim("iat", System.currentTimeMillis() / 1000).claim("clientInfo", info.getClientInfo())
 					.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8")).compact();
 		} else if (type.equals("refreshToken")) {
 			secretKey = CONSTANTS.REFRESH_TOKEN_SECRET_KEY;
-			jwt = Jwts.builder().setExpiration(new Date(43200000)) // 1일 -> 설정정보로 변경해야함
+			jwt = Jwts.builder().setExpiration(new Date(259200000)) // 1일 -> 설정정보로 변경해야함
 					.claim("cmpIp", info.getCmpIp()).claim("homeId", info.getHomeId())
-					.claim("iat", System.currentTimeMillis() / 1000)
+					.claim("iat", System.currentTimeMillis() / 1000).claim("clientInfo", info.getClientInfo())
 					.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8")).compact();
 		}
 
@@ -59,6 +59,10 @@ public class JWTUtils {
 			secretKey = CONSTANTS.REFRESH_TOKEN_SECRET_KEY;
 		}
 		logger.info(token);
+
+		if (token.split("\\.")[0] == null || token.split("\\.")[1] == null || token.split("\\.")[2] == null)
+			return false;
+
 		Decoder decoder = Base64.getDecoder();
 		// 시그니처가 맞는지 확인
 		String enHeader = token.split("\\.")[0];
@@ -70,15 +74,22 @@ public class JWTUtils {
 		logger.info("S= " + signiture);
 		JSONObject obj = new JSONObject(dePayload);
 
+		if (obj.isNull("cmpIp") || obj.isNull("homeId") || obj.isNull("iat") || obj.isNull("clientInfo"))
+			return false;
+
 		String jwt = Jwts.builder().claim("exp", obj.get("exp")) // 1시간 -> 설정정보로 변경해야함
 				.claim("cmpIp", obj.get("cmpIp")).claim("homeId", obj.get("homeId")).claim("iat", obj.get("iat"))
+				.claim("clientInfo", obj.get("clientInfo"))
 				.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8")).compact();
 		logger.info("new = " + jwt);
-		if (token.equals(jwt)) {
-			long iat = Long.parseLong(obj.get("iat").toString());
-			long currentTime = System.currentTimeMillis() / 1000;
-			if (currentTime > iat + Long.parseLong(obj.get("exp").toString())) {
-				return false;
+
+		if (token.equals(jwt)) { // 갱신인 경우에는 시간 검사를 안하기 위해
+			if (type.equals("accessToken")) {
+				long iat = Long.parseLong(obj.get("iat").toString());
+				long currentTime = System.currentTimeMillis() / 1000;
+				if (currentTime > iat + Long.parseLong(obj.get("exp").toString())) {
+					return false;
+				}
 			}
 			return true;
 		}
@@ -97,6 +108,7 @@ public class JWTUtils {
 		JWTInfo jwtInfo = new JWTInfo();
 		jwtInfo.setCmpIp(obj.get("cmpIp").toString());
 		jwtInfo.setHomeId(obj.get("homeId").toString());
+		jwtInfo.setClientInfo(obj.get("clientInfo").toString());
 
 		return jwtInfo;
 	}
@@ -131,4 +143,17 @@ public class JWTUtils {
 		return homeId;
 	}
 
+	static public String getClientInfoFromToken(String token)
+			throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
+
+		Decoder decoder = Base64.getDecoder();
+		String enPayload = token.split("\\.")[1];
+		String dePayload = new String(decoder.decode(enPayload));
+		JSONObject obj = new JSONObject(dePayload);
+
+		String clientInfo = obj.get("clientInfo").toString();
+
+		return clientInfo;
+	}
+
 }

+ 5 - 2
src/main/resources/application.properties

@@ -2,6 +2,8 @@ server.port=8003
 spring.mvc.view.prefix=/WEB-INF/jsp/
 spring.mvc.view.suffix=.jsp
 
+spring.mvc.static-path-pattern=/resources/**
+
 spring.redis.lettuce.pool.max-active=10
 spring.redis.lettuce.pool.max-idle=10
 spring.redis.lettuce.pool.min-idle=2
@@ -14,8 +16,9 @@ spring.jpa.hibernate.ddl-auto=update
 spring.jpa.generate-ddl=true
 spring.datasource.username=admin
 spring.datasource.password = hdci12#$
-
-
+#spring.datasource.username=root
+#spring.datasource.password = 2072712l^^
+#
 server.ssl.key-store=classpath:tomcat.jks
 server.ssl.key-store-password=hdci12#$
 

BIN
src/main/resources/static/fonts/NotoSansCJKkr-Medium.otf


BIN
src/main/resources/static/fonts/NotoSansCJKkr-Regular.otf


BIN
src/main/resources/static/img/arrow.png


BIN
src/main/resources/static/img/incon_ci_ko.png


BIN
src/main/resources/static/img/logo.png


+ 106 - 27
src/main/webapp/WEB-INF/jsp/InsertCode.jsp

@@ -1,5 +1,5 @@
-<%@ page language="java" contentType="text/html; charset=EUC-KR"
-	pageEncoding="EUC-KR"%>
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+	pageEncoding="UTF-8"%>
 <!DOCTYPE html>
 <html>
 <head>
@@ -9,58 +9,137 @@
 	href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
 	integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
 	crossorigin="anonymous">
-<title>»ç¿ëÀÚ ÀÎÁõ</title>
+<title>사용� ��</title>
 <style type="text/css">
+@font-face {
+  font-family: NotoSansCJKkr-Medium;
+  src: url('/resources/fonts/NotoSansCJKkr-Medium.otf');
+}
+
+@font-face {
+  font-family: NotoSansCJKkr-Regular;
+  src: url('/resources/fonts/NotoSansCJKkr-Regular.otf');
+}
+
 body {
-	padding: 30px;
+	background-color: #e6e1da
 }
 
 @media screen and (min-width : 640px) { /* Styles */
-	.myclass {
+	.head {
+		width: 500px;
+		margin: 0 auto;
+	}
+	.content {	
 		width: 500px;
 		margin: 0 auto;
 	}
 }
+
+.content_option {
+	padding-left: 30px;
+	padding-right: 30px;
+}
+
+input[type=text] {
+	border-radius: 3rem;
+	margin-bottom: 10px;
+	height: 50px;
+}
+
+input::placeholder {
+	color: #9e8d76;
+ 	font-size: 20px;
+	font-family: NotoSansCJKkr-Regular;
+}
+
+.button_option {
+	height: 70px;
+	border-radius: 3rem;
+	margin-bottom: 10px;
+	background-color: #9e8d76;
+	color: #ffffff;
+	font-size: 22px;
+}
+
+.title_option {
+	font-family: NotoSansCJKkr-Medium;
+	color: #000000;
+	font-size: 16px;
+}
+
+.input_option {
+font-family: NotoSansCJKkr-Regular;
+	color: #9e8d76;
+	font-size: 20px;
+	display: block;
+    width: 100%;
+    height: calc(1.5em + .75rem + 2px);
+    padding: .375rem .75rem;
+    font-weight: 400;
+    line-height: 1.5;
+    background-color: #fff;
+    background-clip: padding-box;
+    border: 1px solid #ced4da;
+    border-radius: .25rem;
+    transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
+}
+
+
+img {
+	width: 100%;
+}
+
 </style>
 </head>
 <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
 <script type="text/javascript" language="javascript">
+	$(document).ready(function() {
+
+		setInterval(function() {
+			alert("시간� 만료�었습니다.");
+			history.go(-1);
+		}, 180000); // 3000ms(3초)가 경과하면 ozit_timer_test() 함수를 실행합니다.
+
+	});
+
 	function btnClickHandler() {
 		var code = $("#code").val();
 		if (code == "")
-			alert("¿ùÆÐµå¿¡ Ç¥½ÃµÈ ÀÎÁõ¹øÈ£¸¦ ÀÔ·ÂÇϼ¼¿ä.");
+			alert("월패드� 표시� ��번호를 입력하세요.");
 		else {
 			console.log(code);
 			$.ajax({
-					type: "GET",
-					url : "/api/oauth2/validate",
-					data : {
+						type : "GET",
+						url : "/api/oauth2/validate",
+						data : {
 							code : code
-							},
-					success : function(data) {
-											console.log(data);
-											if (data == true) {
-												window.location.href = 'https://teldev.hdc-smart.com:443/api/oauth2/code/generate';
-												} else {
-												alert("ÀÎÁõ ½ÇÆÐ");
-												}
-											}
+						},
+						success : function(data) {
+							console.log(data);
+							if (data == true) {
+								//window.location.href = 'http://127.0.0.1:8003/api/oauth2/code/generate';
+								window.location.href = 'https://teldev.hdc-smart.com:443/api/oauth2/code/generate';
+							} else {
+								alert("�� 실패");
+							}
+						}
 					});
-																						
+
 		}
 	}
 </script>
 <body>
-	<div class = "myclass" align="center">
-		<img src="/resources/img/incon_ci_ko.png"/>
+	<div class="head">
+		<img src="/resources/img/logo.png" />
 	</div>
-	<br>
-	<br>
 	<!-- <form method="post" action="validate"> -->
-	<div class="myclass">
-		<label for="code">ÀÎÁõ¹øÈ£</label> <input type="text" class="form-control"
-			id="code" name="code" placeholder="¿ùÆÐµå¿¡ ¶á ÀÎÁõ¹øÈ£¸¦ ÀÔ·ÂÇϼ¼¿ä." required> <br>
-		<input type="button" class="btn btn-primary btn-block" onClick="javascript:btnClickHandler();"value ="ÀÎÁõÇϱâ"/>
+	<div class="content content_option">
+		<label for="code" class ="title_option">• ��번호</label> 
+		<input type="text" class="input_option"
+			id="code" name="code" placeholder="월패드� 뜬 ��번호를 입력하세요." required>
+		<input type="button" class="btn button_option btn-block"
+			onClick="javascript:btnClickHandler();" value="��하기" />
 	</div>
 </body>
 </html>

+ 112 - 41
src/main/webapp/WEB-INF/jsp/InsertUserInfo.jsp

@@ -1,5 +1,5 @@
-<%@ page language="java" contentType="text/html; charset=EUC-KR"
-	pageEncoding="EUC-KR"%>
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+	pageEncoding="UTF-8"%>
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
 <%
@@ -14,70 +14,141 @@
 	href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
 	integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
 	crossorigin="anonymous">
-<title>»ç¿ëÀÚ ÀÎÁõ</title>
+<title>사용� ��</title>
 <style type="text/css">
+@font-face {
+	font-family: NotoSansCJKkr-Medium;
+	src: url('/resources/fonts/NotoSansCJKkr-Medium.otf');
+}
+
+@font-face {
+	font-family: NotoSansCJKkr-Regular;
+	src: url('/resources/fonts/NotoSansCJKkr-Regular.otf');
+}
 
 body {
-	padding: 30px;
-	
+	background-color: #e6e1da
 }
 
 @media screen and (min-width : 640px) { /* Styles */
-	.myclass {
+	.head {
+		width: 500px;
+		margin: 0 auto;
+	}
+	.content {
 		width: 500px;
 		margin: 0 auto;
 	}
 }
 
+.content_option {
+	padding-left: 30px;
+	padding-right: 30px;
+}
+
+.title_option {
+	font-family: NotoSansCJKkr-Medium;
+	color: #000000;
+	font-size: 16px;
+}
+
+.input_option {
+	font-family: NotoSansCJKkr-Regular;
+	color: #9e8d76;
+	font-size: 20px;
+	display: block;
+	width: 100%;
+	height: calc(1.5em + .75rem + 2px);
+	padding: .375rem .75rem;
+	font-weight: 400;
+	line-height: 1.5;
+	background-color: #fff;
+	background-clip: padding-box;
+	border: 1px solid #ced4da;
+	border-radius: .25rem;
+	transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
+}
+
+input[type=text] {
+	border-radius: 3rem;
+	margin-bottom: 10px;
+	height: 50px;
+}
+
+input::placeholder {
+	color: #9e8d76;
+	font-size: 20px;
+	font-family: NotoSansCJKkr-Regular;
+}
+
+.button_option {
+	height: 70px;
+	border-radius: 3rem;
+	margin-bottom: 10px;
+	background-color: #9e8d76;
+	color: #ffffff;
+	font-size: 22px;
+}
+
+select {
+	background: white;
+}
+
+.select_option {
+	border-radius: 3rem;
+	margin-bottom: 10px;
+	height: 50px;
+	background: white url('/resources/img/arrow.png') no-repeat 95% 50%;
+	background-size: 10px;
+	-webkit-appearance: none;
+}
+
+img {
+	width: 100%;
+}
+
 .test {
 	align: middle;
 }
 
 p {
-	border-radius: .25rem;
-	background-color: #DDDDDD;
 	font-size: 12px;
 	text-align: left;
-	padding: 10px;
+	color: #705b3d;
 }
-
 </style>
 </head>
 
 <body>
-	<div class = "myclass" align="center">
-		<img src="/resources/img/incon_ci_ko.png"/>
+	<div class="head">
+		<img src="/resources/img/logo.png" />
 	</div>
-	<br>
-	<br>
-	<form method="get" action="user/info/submit" class = "myclass">
-
-		<label for="complex">´ÜÁöÁ¤º¸</label> 
-		<select class="form-control" id="complex" name="complex" required>
-			<option value="">´ÜÁö¸í</option>
-			<c:forEach items="${complexInfos}" var="row">
-				<option value="${row.complexIp}">${row.complexName}</option>
-			</c:forEach>
-		</select> 
+	<div class="content content_option">
+		<form method="get" action="user/info/submit">
+
+			<label for="complex" class="title_option">• 단지정보</label> <select
+				class="select_option input_option" id="complex" name="complex"
+				required>
+				<option value=""> 단지명</option>
+				<c:forEach items="${complexInfos}" var="row">
+					<option value="${row.complexIp}" class="select_option input_option">${row.complexName}</option>
+				</c:forEach>
+			</select> <label for="dong" class="title_option">• 세대정보</label> <input
+				type="text" class="input_option" id="dong" name="dong"
+				placeholder=" �" required> <input type="text"
+				class="input_option" id="ho" name="ho" placeholder=" 호" required>
+			<button type="submit" class="button_option btn btn-block">��번호
+				전송하기</button>
+		</form>
 		<br>
-		<label for="dong">¼¼´ëÁ¤º¸</label> 
-		<input type="text" class="form-control" id="dong" name="dong" placeholder="µ¿" required>
-		<br> 
-		<input type="text" class="form-control" id="ho" name="ho" placeholder="È£" required>
-		<br> 
-		<button type="submit" class="btn btn-primary btn-block">ÀÎÁõ¹øÈ£ Àü¼ÛÇϱâ</button>
-	</form>
-	<br>
-	<div class = "myclass" align="center">
-		<p>
-			*ÀÎÁõ¹æ¹ý* 
-			<br>
-			1. ¿ùÆÐµå¿¡¼­ "¼³Á¤">"¸ð¹ÙÀϱâ±âµî·Ï" È­¸é¿¡ ÁøÀÔÇÑ ÈÄ, Áß¾ÓÇÏ´Ü¿¡ "+µî·Ï" ¹öưÀ» ´©¸£¸é ÆË¾÷âÀÌ ¶å´Ï´Ù.
-			<br>
-			2. ÆË¾÷âÀ» ¶ç¿î »óÅ¿¡¼­ ¼¼´ëÁ¤º¸¸¦ ÀÔ·ÂÇϰí "ÀÎÁõ¹øÈ£ Àü¼ÛÇϱâ"¸¦ ´©¸£¸é ÇØ´ç ¼¼´ë ¿ùÆÐµå¿¡ ÀÎÁõ¹øÈ£°¡ Àü¼ÛµË´Ï´Ù.
-			<br>
-			3. ¿ùÆÐµå¿¡ Ãâ·ÂµÈ ÀÎÁõ¹øÈ£¸¦ ÀÔ·ÂÇϰí "ÀÎÁõÇϱâ" ¹öưÀ» ´©¸£¸é ÀÎÁõÀÌ ¿Ï·áµË´Ï´Ù.			
-		</p>
+		<div align="center">
+			<p>
+				[��방법] <br> 1. 월패드�서 "설정">"모바�기기등�" 화면� 진입한 후, 중앙하단� "+등�" 버튼�
+				누르면 �업창� 뜹니다. <br> 2. �업창� �운 �태�서 세대정보를 입력하고 "��번호 전송하기"를 누르면
+				해당 세대 월패드� ��번호가 전송�니다. <br> 3. 월패드� 출력� ��번호를 입력하고 "��하기" 버튼�
+				누르면 ��� 완료�니다.
+			</p>
+		</div>
 	</div>
 
 </body>