// ****Utility 메서드 시작 // Token 값을 비롯한 timestamp 정보를 생성하여 테이블에 추가함. private TokenVO createTokenToTable(RequestAuthVO rVO, UserVO uVO, ClientVO cVO) throws OAuth2Exception { TokenVO tVO; try { // 5.1 Access Token 랜덤하게 생성하여 TokenVO 생성하고 저장 // TODO 목요일 : TOKEN 값 설정하고 DB에 저장 (sqlmap, dao, controller) tVO = new TokenVO(); tVO.setClient_id(rVO.getClient_id()); // 승인한 사용자 tVO.setUserid(uVO.getUserid()); tVO.setToken_type(OAuth2Constant.TOKEN_TYPE_BEARER); tVO.setScope(rVO.getScope()); // expires_in과 refresh_token의 사용 여부는 각 조직이 결정한다. tVO.setExpires_in(OAuth2Constant.EXPIRES_IN_VALUE); tVO.setRefresh_token(OAuth2Util.generateToken()); tVO.setCode(OAuth2Util.generateToken()); tVO.setClient_type(cVO.getClient_type()); tVO.setAccess_token(OAuth2Util.generateToken()); // 생성 시간을 계산을 용이하게 하기 위해 타임스탬프 형태로 저장 long currentTimeStamp = OAuth2Util.getCurrentTimeStamp(); tVO.setCreated_at(currentTimeStamp); tVO.setCreated_rt(currentTimeStamp); System.out.println(tVO); // tbl_Token테이블에 추가 dao.createToken(tVO); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw new OAuth2Exception(401, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } return tVO; }
// grant_type이 authorization_code일 때 private ResponseAccessTokenVO refreshTokenFlow( RequestAccessTokenVO ratVO, HttpServletRequest request) throws OAuth2Exception { // 1. 전달된 refresh Token과 // GET 방식일 때는 Client ID와 Client Secret은 Authorization Header를 통해 전달되어야 함. if (request.getMethod().equalsIgnoreCase("GET")) { String authHeader = (String) request.getHeader("Authorization"); if (authHeader == null || authHeader.equals("")) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_PARAMETER); } // Basic 인증 헤더 파싱 OAuth2Util.parseBasicAuthHeader(authHeader, ratVO); } // 2. ClientID, Secret 모두 전달되었는지 여부 --> 존재 여부 확인 if (ratVO.getClient_id() == null || ratVO.getClient_secret() == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_PARAMETER); } // 3. clientID 와 client_secret의 일치여부 ClientVO cVOTemp = new ClientVO(); cVOTemp.setClient_id(ratVO.getClient_id()); ClientVO cVO = null; try { cVO = dao.getClientOne(cVOTemp); } catch (Exception e) { throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } if (cVO == null) { throw new OAuth2Exception(500, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } if (ratVO.getClient_secret() != null && !cVO.getClient_secret().equals(ratVO.getClient_secret())) { throw new OAuth2Exception(500, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } // 4. refresh token의 일치 여부 if (ratVO.getRefresh_token() == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_PARAMETER); } TokenVO tVOTemp = new TokenVO(); tVOTemp.setRefresh_token(ratVO.getRefresh_token()); TokenVO tVO = null; try { tVO = dao.selectRefreshToken(tVOTemp); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } if (tVO == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_TOKEN); } // 5. TokenVO의 accessToken 갱신 --> DB 업데이트 --> // --> refreshToken 값 없이 ResponseAccessTokenVO객체 생성 --> JSON 포맷으로 응답 tVO.setAccess_token(OAuth2Util.generateToken()); tVO.setCreated_at(OAuth2Util.getCurrentTimeStamp()); try { dao.updateAccessToken(tVO); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } ResponseAccessTokenVO resVO = new ResponseAccessTokenVO( tVO.getAccess_token(), tVO.getToken_type(), tVO.getExpires_in(), null, ratVO.getState(), tVO.getCreated_at()); return resVO; }
// 사용자가 승인 또는 거부 버튼을 클릭하는 경우 @RequestMapping(value = "auth", method = RequestMethod.POST) public String authorizePost( Model model, RequestAuthVO rVO, HttpServletRequest request, HttpServletResponse response) throws OAuth2Exception { // 0. request 값 얻어내기 String isAllow = request.getParameter("isallow"); String userid = request.getParameter("userid"); String password = request.getParameter("password"); // 0.1 로그인한 사용자면 현재사용자와 해당 클라이언트를 위한 Token을 생성해야 함. // 0.2 userid,password로 사용자 정보 조회--> 로그인처리--> 토큰 생성 // 0.3 사용자가 승인을 거부하였다면 사용자에게 알려주고 로그인화면으로 돌아감. if (!isAllow.equals("true")) { return "auth/auth_deny"; } UserVO uVO = null; if (userid != null && password != null) { UserVO uVOTemp = new UserVO(userid, password, "", 0); try { uVO = dao.loginProcess(uVOTemp); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return "auth/auth.jsp"; } } else if (request.getSession().getAttribute("userVO") != null) { uVO = (UserVO) request.getSession().getAttribute("userVO"); } else { throw new OAuth2Exception(401, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } // 1. client_id 존재여부 확인 ClientVO cVOTemp = new ClientVO(); cVOTemp.setClient_id(rVO.getClient_id()); System.out.println("## rVO ClientID : " + rVO.getClient_id()); ClientVO cVO; try { cVO = dao.getClientOne(cVOTemp); } catch (Exception e) { // TODO Auto-generated catch block throw new OAuth2Exception(400, OAuth2ErrorConstant.SERVER_ERROR); } if (cVO == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } // 2. scope 포함여부 확인 if (!OAuth2Scope.isScopeValid(rVO.getScope(), cVO.getScope())) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_SCOPE); } // 3. redirect_uri 일치여부 확인 if (!rVO.getRedirect_uri().equals(cVO.getRedirect_uri())) { throw new OAuth2Exception(400, OAuth2ErrorConstant.NOT_MATCH_REDIRECT_URI); } // 4. token, code 생성하여 테이블에 추가, // refresh token을 사용하지 않을 경우는 테이블에 저장된 값에서 code 필드값만 사용함 TokenVO tVO = createTokenToTable(rVO, uVO, cVO); // 5. response_type 확인하고 code, token인 경우에 따라 각기 다른 흐름 처리 String response_type = rVO.getResponse_type(); String redirect = ""; if (response_type.equals(OAuth2Constant.RESPONSE_TYPE_CODE)) { // 6.1 4번 단계에서 생성된 값들 중 code 값을 이용해 redirect 함. // redirect_uri?code=XXXXXXXXX redirect = "redirect:" + rVO.getRedirect_uri() + "?code=" + tVO.getCode(); if (rVO.getState() != null) { redirect += "&state=" + rVO.getState(); } } else if (response_type.equals(OAuth2Constant.RESPONSE_TYPE_TOKEN)) { // useragent 흐름 ResponseAccessTokenVO tokenVO = null; if (OAuth2Constant.USE_REFRESH_TOKEN) { tokenVO = new ResponseAccessTokenVO( tVO.getAccess_token(), tVO.getToken_type(), tVO.getExpires_in(), tVO.getRefresh_token(), rVO.getState(), tVO.getCreated_at()); } else { // OAuth2AccessToken 클래스 기능을 이용해 정해진 규칙에 의해 생성함. tokenVO = new ResponseAccessTokenVO( this.tokenService.generateAccessToken( cVO.getClient_id(), cVO.getClient_secret(), uVO.getUserid(), password), OAuth2Constant.TOKEN_TYPE_BEARER, 0, null, rVO.getState(), tVO.getCreated_at()); tokenVO.setExpires_in(0); tokenVO.setRefresh_token(null); } String acc = OAuth2Util.getAccessTokenToFormUrlEncoded(tokenVO); redirect = "redirect:" + rVO.getRedirect_uri() + "#" + acc; } else { throw new OAuth2Exception(400, OAuth2ErrorConstant.UNSUPPORTED_RESPONSE_TYPE); } return redirect; }
// grant_type이 authorization_code일 때 private ResponseAccessTokenVO accessTokenServerFlow( RequestAccessTokenVO ratVO, HttpServletRequest request) throws OAuth2Exception { // GET 방식일 때는 Client ID와 Client Secret은 Authorization Header를 통해 전달되어야 함. System.out.println("### token flow 2"); if (request.getMethod().equalsIgnoreCase("GET")) { String authHeader = (String) request.getHeader("Authorization"); if (authHeader == null || authHeader.equals("")) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_PARAMETER); } // Basic 인증 헤더 파싱 OAuth2Util.parseBasicAuthHeader(authHeader, ratVO); } // 1. ClientID, Secret 모두 전달되었는지 여부 --> 존재 여부 확인 System.out.println("### token flow 3"); if (ratVO.getClient_id() == null || ratVO.getClient_secret() == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_PARAMETER); } ClientVO cVOTemp = new ClientVO(); cVOTemp.setClient_id(ratVO.getClient_id()); cVOTemp.setClient_secret(ratVO.getClient_secret()); ClientVO cVO = null; try { cVO = dao.getClientOne(cVOTemp); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } // 1.2 client 존재 여부 확인 System.out.println("### token flow 4"); if (cVO == null) { throw new OAuth2Exception(401, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } // 2. redirect_uri 일치 여부 확인 System.out.println("### token flow 5"); if (!ratVO.getRedirect_uri().equals(cVO.getRedirect_uri())) { throw new OAuth2Exception(400, OAuth2ErrorConstant.NOT_MATCH_REDIRECT_URI); } // 3. code값 일치 여부 확인 System.out.println("### token flow 6"); if (ratVO.getCode() == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_PARAMETER); } TokenVO tVOTemp = new TokenVO(); tVOTemp.setCode(ratVO.getCode()); TokenVO tVO = null; try { tVO = dao.selectTokenByCode(tVOTemp); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } if (tVO == null) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_CODE); } // 4. expire되었는지 여부 확인, refresh token 사용 여부에 따라 결정함. if (OAuth2Constant.USE_REFRESH_TOKEN) { System.out.println("### token flow 7"); long expires = tVO.getCreated_at() + tVO.getExpires_in(); if (System.currentTimeMillis() > expires) { // 토큰 생성되고 한참 뒤에 code로 access token을 요청하는 것은 금지 throw new OAuth2Exception(400, OAuth2ErrorConstant.EXPIRED_TOKEN); } } // 5. 전달받은 state값 그대로 전달(CSRF 공격 방지용) // 6. ResponseAccessToken객체 생성 --> json 포맷 응답 System.out.println("### token flow 9"); ResponseAccessTokenVO resVO = new ResponseAccessTokenVO(); resVO.setIssued_at(tVO.getCreated_at()); resVO.setState(ratVO.getState()); resVO.setToken_type(tVO.getToken_type()); if (OAuth2Constant.USE_REFRESH_TOKEN) { resVO.setAccess_token(tVO.getAccess_token()); resVO.setExpires_in(tVO.getExpires_in()); resVO.setRefresh_token(tVO.getRefresh_token()); } else { // 6.1. password를 확보하기 위해 UserVO 객체 획득 // ResponsToken을 생성하고 나면 token 테이블의 레코드 삭제 UserVO uVOTemp = new UserVO(); uVOTemp.setUserid(tVO.getUserid()); UserVO uVO = null; try { uVO = dao.getUserInfo(uVOTemp); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } if (uVO == null) { throw new OAuth2Exception(500, OAuth2ErrorConstant.INVALID_USER); } // token 테이블 레코드 삭제 try { dao.deleteToken(tVO); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } resVO.setAccess_token( this.tokenService.generateAccessToken( cVO.getClient_id(), cVO.getClient_secret(), uVO.getUserid(), uVO.getPassword())); } return resVO; }
@RequestMapping(value = "auth", method = RequestMethod.GET) public ModelAndView authorize( RequestAuthVO vo, HttpServletResponse response, HttpServletRequest request) throws OAuth2Exception { ModelAndView mav = new ModelAndView(); HttpSession session = request.getSession(); UserVO loginnedVO = (UserVO) session.getAttribute("userVO"); // 1. 현재 로그인한 상태인지 확인 if (loginnedVO != null) { // 이미 로그인한 상황이면 승인버튼만 보여주도록 해야 함. mav.addObject("isloginned", true); } else { mav.addObject("isloginned", false); } System.out.println("## server flow 2.1"); // 2.1 전달된 client_id가 유효한 client_id인지 확인 ClientVO clientVO1 = new ClientVO(); clientVO1.setClient_id(vo.getClient_id()); ClientVO cVO = null; try { cVO = dao.getClientOne(clientVO1); } catch (Exception e) { e.printStackTrace(); throw new OAuth2Exception(500, OAuth2ErrorConstant.SERVER_ERROR); } if (cVO == null) { throw new OAuth2Exception(401, OAuth2ErrorConstant.UNAUTHORIZED_CLIENT); } System.out.println("## server flow 2.2"); System.out.println(vo.getResponse_type()); // 2.2 response_type이 code일 때는 client_secret이 일치하는지도 확인함. if (!vo.getResponse_type().equals(OAuth2Constant.RESPONSE_TYPE_CODE) && !vo.getResponse_type().equals(OAuth2Constant.RESPONSE_TYPE_TOKEN)) { throw new OAuth2Exception(401, OAuth2ErrorConstant.UNSUPPORTED_RESPONSE_TYPE); } System.out.println("## server flow 3"); // 3. 요청으로 전달된 scope가 client 등록시 포함된 scope에 존재하는지 여부 확인 if (!OAuth2Scope.isScopeValid(vo.getScope(), cVO.getScope())) { throw new OAuth2Exception(400, OAuth2ErrorConstant.INVALID_SCOPE); } System.out.println("## server flow 4"); // 4. grant_type이 유효한지 확인 String gt = vo.getResponse_type(); if (!gt.equals(OAuth2Constant.RESPONSE_TYPE_CODE) && !gt.equals(OAuth2Constant.RESPONSE_TYPE_TOKEN)) { throw new OAuth2Exception(400, OAuth2ErrorConstant.UNSUPPORTED_RESPONSE_TYPE); } System.out.println("## server flow 5"); // 5 최종적으로 유효하다면 auth/auth.jsp 를 보여줌. mav.addObject("requestAuthVO", vo); mav.addObject("clientVO", cVO); mav.setViewName("auth/auth"); return mav; }