class AuthCodeGrantController {

    @RequestMapping(path = RequestPath.PATH_OAUTH_AUTHORIZE_CODE, method = {GET, POST})
    public ModelAndView authorize(HttpServletRequest request, HttpServletResponse response, HttpSession session,
                                  @RequestParam("response_type") String responseType,
                                  @RequestParam("client_id") long clientId,
                                  @RequestParam("redirect_uri") String redirectUri,
                                  @RequestParam(name = "scope", required = false) String scope,
                                  @RequestParam(name = "state", required = false) String state,
                                  @RequestParam(name = "skip_confirm", required = false, defaultValue = "false") boolean skipConfirm,
                                  @RequestParam(name = "force_login", required = false, defaultValue = "false") boolean forceLogin)
            throws VerificationException {

        log.info("Request authorize code, appId[{}]", clientId);
        ModelAndView mav = new ModelAndView();

        // 请求参数封装与校验
        AuthorizeRequestParams requestParams = new AuthorizeRequestParams(responseType, clientId, redirectUri, scope, state);
        ErrorCode validateResult = requestParams.validate();
        if (!ErrorCode.NO_ERROR.equals(validateResult)) {
            // 请求参数有误
            log.error("Request authorize params error, appId[{}], errorCode[{}], params[{}]", clientId, validateResult, requestParams);
            if (ErrorCode.INVALID_CLIENT.equals(validateResult) || ErrorCode.INVALID_REDIRECT_URI.equals(validateResult)) {
                /*
                 * If the request fails due to a missing, invalid, or mismatching redirection URI,
                 * or if the client identifier is missing or invalid, the authorization server SHOULD inform the resource owner of the
                 * error and MUST NOT automatically redirect the user-agent to the invalid redirection URI.
                 */
                return JsonView.render(new ResultInfo(validateResult, state), response, false);
            }
            return ResponseUtils.buildErrorResponse(redirectUri, validateResult, state);
        }

        AppInfo appInfo = requestParams.getAppInfo();
        UserInfo user = SessionUtils.getUser(session, CookieUtils.get(request, COOKIE_KEY_USER_LOGIN_SIGN));
        if (null == user || forceLogin) {
            // 用户未登录或需要强制登录，跳转到登录页面
            return ResponseUtils.buildLoginResponse(request, appInfo);
        }
        requestParams.setUserInfo(user);

        // 获取用户与APP之间的授权关系记录
        Optional<AuthorizeRelation> relation =
                authorizeRelationService.getAuthorizeRelation(user.getId(), requestParams.getClientId(), requestParams.getScope());

        if (relation.isPresent() && skipConfirm) {
            // 用户已授权该APP，下发授权码
            log.info("User had authorized and issue auth code, appId[{}], userId[{}], scope[{}]", clientId, user.getId(), requestParams.getScope());
            AuthorizationCode code = new AuthorizationCode(
                    requestParams.getAppInfo(), user.getId(), relation.get().getScope(), requestParams.getRedirectUri());
            String key = code.getValue();
            if (StringUtils.isBlank(key)) {
                log.error("Generate auth code error, appId[{}], userId[{}], scope[{}]", clientId, user.getId(), requestParams.getScope());
                return ResponseUtils.buildErrorResponse(redirectUri, ErrorCode.AUTHORIZATION_CODE_GENERATE_ERROR, state);
            }
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(requestParams.getRedirectUri());
            builder.queryParam("code", key);
            if (StringUtils.isNotEmpty(state)) {
                builder.queryParam("state", state);
            }
            // cache auth code
            AuthCodeCacheHandler.getInstance().put(key, code);
            mav.setViewName("redirect:" + builder.toUriString());
            return mav;
        }

        // 用户未授权该APP，跳转到授权页面
        log.info("User not authorized and goto authorize page, appId[{}], userId[{}], relationExist[{}], skipConfirm[{}]",
                clientId, user.getId(), relation.isPresent(), skipConfirm);
        List<Scope> scopes = scopeService.getScopes(requestParams.getScope());
        return ResponseUtils.buildAuthorizeResponse(request, scopes, user, appInfo, state);
    }

}