123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- package com.icontrols.oauth.controller;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.security.GeneralSecurityException;
- import java.security.NoSuchAlgorithmException;
- import java.time.Duration;
- import java.util.Base64;
- 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;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.http.HttpEntity;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.RequestHeader;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.client.RestTemplate;
- import org.springframework.web.server.ResponseStatusException;
- import org.springframework.web.servlet.ModelAndView;
- import org.springframework.web.servlet.view.RedirectView;
- import com.icontrols.oauth.constants.ERROR;
- import com.icontrols.oauth.model.ClientInfo;
- import com.icontrols.oauth.model.ComplexInfo;
- import com.icontrols.oauth.model.JWTInfo;
- import com.icontrols.oauth.model.Token;
- 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.JWTUtils;
- @RestController
- @RequestMapping(value = "/api/oauth2")
- public class OAuthController {
- private static final Logger logger = LoggerFactory.getLogger(OAuthController.class);
- @Autowired
- ClientInfoRepository clientInfoRepo;
- @Autowired
- ComplexInfoRepository complexInfoRepo;
- @Autowired
- RedisTemplate<String, Object> redisTemplate;
- // 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 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,
- @RequestParam(value = "redirect_uri", required = true) String redirectUri, HttpSession httpSession,
- Model model) {
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("[STEP #1] /api/oauth2/authorize");
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("*[parameters]");
- logger.info("*1. response_type: " + responseType);
- logger.info("*2. client_id: " + clientId);
- logger.info("*3. state: " + state);
- logger.info("*4. scope: " + scope);
- logger.info("*5. redirect_uri: " + redirectUri);
- ModelAndView mav = new ModelAndView();
- ClientInfo clientInfo = clientInfoRepo.findByClientId(clientId);
- if (clientInfo == null) {
- throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, ERROR.INVALID_CLIENT);
- } else {
- logger.info("*[clientinfo]");
- logger.info("*" + clientInfo.toString());
- if (!responseType.equals("code")) {
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.UNSUPPORTED_GRANT_TYPE);
- } else {
- httpSession.setAttribute("redirectUri", redirectUri);
- httpSession.setAttribute("state", state);
- // TODO scope != null -> httpSession.setAttribute("scope", scope);
- List<ComplexInfo> complexInfos = complexInfoRepo.findAll();
- model.addAttribute("complexInfos", complexInfos);
- // \src\main\webapp\WEB-INF\jsp\InsertUserInfo.jsp
- mav.setViewName("InsertUserInfo");
- }
- }
- return mav;
- }
- @RequestMapping(value = "/user/info/submit", method = RequestMethod.GET)
- public ModelAndView sendCode(HttpSession httpSession, HttpServletResponse httpResponse,
- @RequestParam(value = "complex", required = true) String complex,
- @RequestParam(value = "dong", required = true) String dong,
- @RequestParam(value = "ho", required = true) String ho)
- throws IOException, NoSuchAlgorithmException, GeneralSecurityException {
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("[STEP #2] /api/oauth2/user/submit");
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("*[parameters]");
- logger.info("*1. complex: " + complex);
- logger.info("*2. dong: " + dong);
- logger.info("*3. ho: " + ho);
- // TODO 단지서버로 인증번호 전송해야함.
- // wallpad 코드 생성
- Random generator = new Random();
- String wallpadCode = "";
- 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);
- while (!vop.setIfAbsent(wallpadCode, homeInfo, d)) {
- wallpadCode = "";
- for (int i = 0; i < 6; i++) {
- wallpadCode += Integer.toString(generator.nextInt(10));
- }
- }
- 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";
- logger.info(url);
- // TODO 예외처리 단지통신
- HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
- factory.setReadTimeout(3000);
- factory.setConnectTimeout(3000);
- RestTemplate template = new RestTemplate(factory);
- HttpEntity<Object> entity = new HttpEntity<Object>(body);
- try {
- String answer = template.postForObject(url, entity, String.class);
- logger.info(answer);
- } catch (Exception e) {
- new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.INVALID_HOMEINFO);
- }
- ModelAndView mav = new ModelAndView();
- mav.setViewName("InsertCode");
- return mav;
- }
- // InsertCode.jsp에서 월패드 인증번호를 입력받아 ajax로 실행하는 부분,
- // 사용자가 입력한 인증번호의 유효성을 검증함.
- @RequestMapping(value = "/validate", method = RequestMethod.GET)
- public @ResponseBody Boolean validate(HttpSession httpSession,
- @RequestParam(value = "code", required = true) String wallpadCode) {
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("[STEP #3] /api/oauth2/auth");
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("*[parameters]");
- logger.info("*1. Input code: " + wallpadCode);
- ValueOperations<String, Object> vop = redisTemplate.opsForValue();
- if (vop.get(wallpadCode) == null) { // 없으면 null
- return false;
- }
- httpSession.setAttribute("homeId", vop.get(wallpadCode));
- return true;
- }
- // 월패드 인증이 실행된 경우, 인증 코드를 발급해 리다이렉트하는 부분
- @RequestMapping(value = "/code/generate", method = RequestMethod.GET)
- public RedirectView validateCode(HttpSession httpSession) {
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("[STEP #4] /code/generate");
- logger.info("**************************************************************");
- logger.info("**************************************************************");
- logger.info("*[parameters]");
- logger.info("*1. home id: " + httpSession.getAttribute("homeId"));
- String code = "";
- String homeInfo = "";
- ValueOperations<String, Object> vop = redisTemplate.opsForValue();
- // 코드 발급
- homeInfo = httpSession.getAttribute("homeId").toString();
- logger.info("*[home info]");
- logger.info("*1. homeInfo: " + homeInfo);
- code = "";
- Random rnd = new Random();
- for (int i = 0; i < 10; i++) {
- code += String.valueOf((char) ((int) (rnd.nextInt(26)) + 65));
- }
- logger.info("*[code]");
- logger.info("*1. code: " + code);
- Duration d = Duration.ofSeconds(180);
- 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");
- redirectUri += "&code=" + code;
- RedirectView redirectView = new RedirectView();
- redirectView.setUrl(redirectUri);
- return redirectView;
- }
- // 토큰 발급하는 부분
- @RequestMapping(value = "/token", method = RequestMethod.POST, produces = "application/json")
- public @ResponseBody Token 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,
- @RequestHeader HttpHeaders headers)
- throws NoSuchAlgorithmException, UnsupportedEncodingException, GeneralSecurityException {
- logger.info("[STEP #5] /api/oauth2/token");
- // TODO Code유효성 검사
- // TODO token 발급
- logger.info(code);
- logger.info(grantType);
- ValueOperations<String, Object> vop = redisTemplate.opsForValue();
- String newAccessToken = ""; // 생성해야 함.
- String newRefreshToken = ""; // 생성해야 함.
- Token token = new Token();
- if (grantType.equals("authorization_code")) {
- // TODO 코드 검사
- if (vop.get(code) == null) { // 인증코드가 없는 경우
- // 에러페이지 리턴
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.INVALID_GRANT);
- } else {
- AES256Util aes = new AES256Util();
- String homeInfo = vop.get(code).toString();
- String jwt_homeInfo = homeInfo.split("/")[1] + "/" + homeInfo.split("/")[2];
- String encodedHomeInfo = aes.encrypt(jwt_homeInfo);
- logger.info("encodedHomeInfo =" + encodedHomeInfo);
- logger.info("encoded = " + encodedHomeInfo);
- String complexIp = homeInfo.split("/")[0];
- Encoder encoder = Base64.getEncoder();
- complexIp = new String(encoder.encode(complexIp.getBytes()));
- JWTInfo jwtinfo = new JWTInfo(complexIp, encodedHomeInfo);
- newAccessToken = JWTUtils.generateToken(jwtinfo, "accessToken");
- newRefreshToken = JWTUtils.generateToken(jwtinfo, "refreshToken");
- }
- } 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 {
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ERROR.INVALID_REFRESHTOKEN);
- }
- }
- } else {
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
- }
- token.setAccess_token(newAccessToken);
- token.setRefresh_token(newRefreshToken);
- // class 만들어서 사용하는 것으로 변경해야함.
- logger.info(token.toString());
- return token;
- }
- 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]));
- 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
- // 여기는 리다이렉트 받는 URl임 테스트용. Client를 대신함.
- @RequestMapping(value = "/redirect", method = RequestMethod.GET)
- public @ResponseBody String RedirectTester(HttpSession httpSession,
- @RequestParam(value = "code", required = true) String code) {
- logger.info("/api/oauth2/redirect");
- String redirectUri = "http://61.33.215.56:8003/api/oauth2/token?grant_type=authorization_code&code=" + code;
- RedirectView redirectView = new RedirectView();
- redirectView.setUrl(redirectUri);
- return code;
- }
- }
|