|
@@ -13,6 +13,7 @@ import java.util.Random;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import javax.servlet.http.HttpSession;
|
|
|
|
|
|
+import org.json.JSONObject;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
@@ -43,7 +44,9 @@ 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;
|
|
|
+import com.sun.net.httpserver.HttpServer;
|
|
|
|
|
|
@RestController
|
|
|
@RequestMapping(value = "/api/oauth2")
|
|
@@ -61,7 +64,7 @@ public class OAuthController {
|
|
|
|
|
|
// http://127.0.0.1:8080/api/oauth2/authorize?client_id=clientid&redirect_uri=http://127.0.0.1:8080/api/oauth2/redirect&scope=scope&response_type=code&state=state
|
|
|
@RequestMapping(value = "/authorize", method = RequestMethod.GET)
|
|
|
- public @ResponseBody ModelAndView Authorize(@RequestHeader HttpHeaders headers,
|
|
|
+ public ModelAndView Authorize(@RequestHeader HttpHeaders headers,
|
|
|
@RequestParam(value = "response_type", required = true) String responseType,
|
|
|
@RequestParam(value = "client_id", required = true) String clientId,
|
|
|
@RequestParam(value = "state", required = true) String state, @RequestParam(value = "scope") String scope,
|
|
@@ -76,8 +79,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 +101,14 @@ 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);
|
|
|
+// if (scope != null)
|
|
|
+// httpSession.setAttribute("scope", scope);
|
|
|
+
|
|
|
List<ComplexInfo> complexInfos = complexInfoRepo.findAll();
|
|
|
model.addAttribute("complexInfos", complexInfos);
|
|
|
|
|
@@ -109,8 +121,8 @@ public class OAuthController {
|
|
|
return mav;
|
|
|
}
|
|
|
|
|
|
- @RequestMapping(value = "/user/info/submit", method = RequestMethod.POST)
|
|
|
- public ModelAndView sendCode(HttpSession httpSession, HttpServletResponse httpResponse,
|
|
|
+ @RequestMapping(value = "/user/info/submit", method = RequestMethod.GET)
|
|
|
+ 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 +138,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,20 +146,30 @@ public class OAuthController {
|
|
|
for (int i = 0; i < 6; i++) {
|
|
|
wallpadCode += Integer.toString(generator.nextInt(10));
|
|
|
}
|
|
|
+
|
|
|
logger.info("*[wallpadCode]");
|
|
|
logger.info("*code: " + wallpadCode);
|
|
|
|
|
|
-// http://61.33.215.54:8002/iotservice/code/request
|
|
|
-// {
|
|
|
-// "dong":"802",
|
|
|
-// "ho": "704",
|
|
|
-// "code" :"931206",
|
|
|
-// }
|
|
|
+ // 세대정보 인코딩
|
|
|
+ String homeInfo = complex + "/" + dong + "/" + ho + "/" + clientId;
|
|
|
+
|
|
|
+ // 2019.05.02 DB저장에서 세션 저장 방식으로 변경
|
|
|
+ httpSession.setAttribute("wallpadCode", wallpadCode);
|
|
|
+ httpSession.setAttribute("homeInfo", homeInfo);
|
|
|
+
|
|
|
+ logger.info("*[redis]");
|
|
|
+ logger.info("*(key,value): (" + wallpadCode + "," + homeInfo + ")");
|
|
|
|
|
|
// 단지서버로 월패드인증번호 전송
|
|
|
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 예외처리 단지통신
|
|
@@ -163,15 +185,6 @@ public class OAuthController {
|
|
|
new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.INVALID_HOMEINFO);
|
|
|
}
|
|
|
|
|
|
- // 세대정보 인코딩
|
|
|
- String homeInfo = complex + "/" + dong + "/" + ho;
|
|
|
- // db에 저장
|
|
|
- ValueOperations<String, Object> vop = redisTemplate.opsForValue();
|
|
|
- Duration d = Duration.ofSeconds(180);
|
|
|
- vop.set(wallpadCode, homeInfo, d);
|
|
|
- logger.info("*[redis]");
|
|
|
- logger.info("*(key,value): (" + wallpadCode + "," + homeInfo + ")");
|
|
|
-
|
|
|
ModelAndView mav = new ModelAndView();
|
|
|
mav.setViewName("InsertCode");
|
|
|
return mav;
|
|
@@ -190,12 +203,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;
|
|
|
}
|
|
|
|
|
@@ -208,14 +234,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 = "";
|
|
@@ -227,7 +253,13 @@ public class OAuthController {
|
|
|
logger.info("*1. code: " + code);
|
|
|
|
|
|
Duration d = Duration.ofSeconds(180);
|
|
|
- vop.set(code, homeInfo, d);
|
|
|
+
|
|
|
+ while (!vop.setIfAbsent(code, homeInfo, d)) {
|
|
|
+ code = "";
|
|
|
+ for (int i = 0; i < 10; i++) {
|
|
|
+ code += String.valueOf((char) ((int) (rnd.nextInt(26)) + 65));
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
String redirectUri = (String) httpSession.getAttribute("redirectUri");
|
|
|
redirectUri += "?state=" + httpSession.getAttribute("state");
|
|
@@ -240,21 +272,23 @@ public class OAuthController {
|
|
|
}
|
|
|
|
|
|
// 토큰 발급하는 부분
|
|
|
- @RequestMapping(value = "/token", method = RequestMethod.GET, produces = "application/json")
|
|
|
- public @ResponseBody Token Token(@RequestParam(value = "grant_type", required = true) String grantType,
|
|
|
+ @RequestMapping(value = "/token", method = RequestMethod.POST, produces = "application/json")
|
|
|
+ public @ResponseBody String Token(@RequestParam(value = "grant_type", required = true) String grantType,
|
|
|
@RequestParam(value = "code", required = false) String code,
|
|
|
- @RequestParam(value = "refresh_token", required = false) String refreshToken, HttpSession httpSession)
|
|
|
+ @RequestParam(value = "refresh_token", required = false) String refreshToken, HttpSession httpSession,
|
|
|
+ @RequestHeader HttpHeaders headers, HttpServletResponse response)
|
|
|
throws NoSuchAlgorithmException, UnsupportedEncodingException, GeneralSecurityException {
|
|
|
|
|
|
+ response.setStatus(HttpServletResponse.SC_OK);
|
|
|
logger.info("[STEP #5] /api/oauth2/token");
|
|
|
// TODO Code유효성 검사
|
|
|
// TODO token 발급
|
|
|
- logger.info(code);
|
|
|
- logger.info(grantType);
|
|
|
+ logger.info("grant_type: " + grantType);
|
|
|
ValueOperations<String, Object> vop = redisTemplate.opsForValue();
|
|
|
|
|
|
String newAccessToken = ""; // 생성해야 함.
|
|
|
String newRefreshToken = ""; // 생성해야 함.
|
|
|
+ String answer = null; // 단지서버에 토큰 관련요청 후 수신하는 응답
|
|
|
Token token = new Token();
|
|
|
if (grantType.equals("authorization_code")) {
|
|
|
// TODO 코드 검사
|
|
@@ -273,39 +307,133 @@ 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();
|
|
|
+
|
|
|
+ answer = DanziAuthUtils.create(token, url);
|
|
|
+
|
|
|
}
|
|
|
} else if (grantType.equals("refresh_token")) {
|
|
|
- // TODO refreshToken 검사
|
|
|
- // jwt토큰 디코딩해서 내역 검사해야됨.
|
|
|
- // 시그니처 확인해야됨
|
|
|
if (refreshToken != null) {
|
|
|
- Boolean bool = JWTUtils.validateToken(refreshToken, "refreshToken");
|
|
|
- logger.info(bool.toString());
|
|
|
- if (bool) {
|
|
|
- // TODO token의 payload에서 compelxCd, homeId 가져오고 새로 토큰 생성한다
|
|
|
-
|
|
|
- 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();
|
|
|
+ String isValidToken = DanziAuthUtils.get(url, refreshToken);
|
|
|
+ logger.info(isValidToken);
|
|
|
+ JSONObject isValidTokenJson = new JSONObject(isValidToken);
|
|
|
+ logger.info(isValidTokenJson.toString());
|
|
|
+
|
|
|
+ if (!isValidTokenJson.get("result").toString().equalsIgnoreCase("success")) {
|
|
|
+
|
|
|
+ if (isValidTokenJson.getJSONObject("error").getInt("code") == -200) // 서버에러인경우
|
|
|
+ throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
+
|
|
|
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
|
|
+ logger.info(isValidTokenJson.get("result").toString());
|
|
|
+ JSONObject error = new JSONObject();
|
|
|
+ error.put("error", "invalid_grant");
|
|
|
+ error.put("error_description", "invalid token");
|
|
|
+ return error.toString();
|
|
|
}
|
|
|
+
|
|
|
+ // 유효한 토큰인 경우 토큰 생성
|
|
|
+ JWTInfo jwtinfo = JWTUtils.getJWTInfoFromToken(refreshToken);
|
|
|
+ newAccessToken = JWTUtils.generateToken(jwtinfo, "accessToken");
|
|
|
+ newRefreshToken = JWTUtils.generateToken(jwtinfo, "refreshToken");
|
|
|
+ token.setAccess_token(newAccessToken);
|
|
|
+ token.setRefresh_token(newRefreshToken);
|
|
|
+
|
|
|
+ // 단지서버에 갱신된 토큰 전달
|
|
|
+ answer = DanziAuthUtils.refresh(url, token, refreshToken);
|
|
|
+ logger.info(answer);
|
|
|
+
|
|
|
}
|
|
|
} else {
|
|
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
|
|
}
|
|
|
|
|
|
- token.setAccess_token(newAccessToken);
|
|
|
- token.setRefresh_token(newRefreshToken);
|
|
|
+ // 갱신된 토큰 전달에 대한 응답처리
|
|
|
+ logger.info(answer);
|
|
|
+ if (answer == null) {
|
|
|
+ throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
+ } else {
|
|
|
+ JSONObject obj = new JSONObject(answer);
|
|
|
+ logger.info(obj.get("result").toString());
|
|
|
+ if (!obj.get("result").toString().equalsIgnoreCase("success")) {
|
|
|
+
|
|
|
+ if (obj.getJSONObject("error").getInt("code") == -200) // 서버에러인경우
|
|
|
+ throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
+
|
|
|
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
|
|
+ JSONObject error = new JSONObject();
|
|
|
+ error.put("error", "invalid_grant");
|
|
|
+ error.put("error_description", "invalid token");
|
|
|
+ return error.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// class 만들어서 사용하는 것으로 변경해야함.
|
|
|
logger.info(token.toString());
|
|
|
|
|
|
- return token;
|
|
|
+ // TODO 단지서버로 전송하고 정상 응답 받은 경우에만 정상리턴 -> 아닌경우에는 500
|
|
|
+ // 토큰 생성인 경우와 갱신인 경우 구분해서 처리 create, refresh
|
|
|
+
|
|
|
+ JSONObject tokenResult = new JSONObject();
|
|
|
+ tokenResult.put("access_token", token.getAccess_token());
|
|
|
+ tokenResult.put("refresh_token", token.getRefresh_token());
|
|
|
+ tokenResult.put("token_type", token.getToken_type());
|
|
|
+ tokenResult.put("expires_in", token.getExpires_in());
|
|
|
+
|
|
|
+ return tokenResult.toString();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean validateHeaderAuth(String authorization) {
|
|
|
+ String splitStr[] = authorization.split(" ");
|
|
|
|
|
|
+ // splitStr length 가 2가 아니면 유효하지 않은 authorization 값
|
|
|
+ if (splitStr.length != 2)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // splitStr이 Basic이 아니어도 에러
|
|
|
+ if (!splitStr[0].equals("Basic"))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // Base64 디코드
|
|
|
+ String decodeStr = new String(Base64.getDecoder().decode(splitStr[1].trim()));
|
|
|
+
|
|
|
+ splitStr = decodeStr.split(":");
|
|
|
+ // splitStr length 가 2가 아니면 유효하지 않은 authorization 값
|
|
|
+ if (splitStr.length != 2)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ ClientInfo clientInfo = clientInfoRepo.findByClientId(splitStr[0]);
|
|
|
+ if (clientInfo == null)
|
|
|
+ return false;
|
|
|
+ if (!clientInfo.getClientSecret().equals(splitStr[1]))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
// ((Test)) Redirect Destination
|
|
@@ -321,4 +449,10 @@ public class OAuthController {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @RequestMapping(value = "/aws/healthcheck", method = RequestMethod.GET)
|
|
|
+ public void healthCheck(HttpEntity<String> httpEntity) {
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
}
|