package im.zksy.yjmqserver.common.shiro.filter;

import im.zksy.yjmqserver.common.constants.AuthcTypeEnum;
import im.zksy.yjmqserver.common.exception.UnknownRedirectUrlException;
import im.zksy.yjmqserver.common.shiro.OAuth2Helper;
import im.zksy.yjmqserver.common.shiro.token.OAuth2Token;
import im.zksy.yjmqserver.common.util.WebHelper;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * OAuth2 认证过滤器
 */
public class OAuth2AuthenticationFilter extends AuthenticatingFilter {

    private final OAuth2Helper oAuth2Helper;

    // oauth2 authc code 参数名
    private static final String AUTHC_CODE_PARAM = "code";

    public OAuth2AuthenticationFilter(OAuth2Helper oAuth2Helper) {
        this.oAuth2Helper = oAuth2Helper;
    }

    /**
     * 使用 OAuth2 服务提供商返回的 code 构建 token
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String code = httpRequest.getParameter(AUTHC_CODE_PARAM);

        // 根据回调地址, 判断当前的认证类型是什么
        String requestURI = httpRequest.getRequestURI();
        AuthcTypeEnum authcType = oAuth2Helper.getAuthcTypeByRedirectUrl(requestURI);

        if (authcType == null) {
            throw new UnknownRedirectUrlException("未知的回调地址:" + requestURI);
        }

        // 创建 token 到 realm 中执行.
        return new OAuth2Token(code, authcType);
    }

    /**
     * 是否允许访问, 直接返回为不允许, 因为本页面只是为了进行认证, 并跳转到首页.
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return false;
    }

    /**
     * 当 isAccessAllowed 不允许访问时, 判断 oauth2 服务提供商是否返回了错误信息 <p>
 *     如果没有返回错误信息, 则判断
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        String error = request.getParameter("error");
        String errorDescription = request.getParameter("error_description");

        if (!StringUtils.isEmpty(error)) { // 如果服务端返回了错误
            WebUtils.issueRedirect(request, response, "/error?error=" + error + "error_description=" + errorDescription);
            return false;
        }

        if (StringUtils.isEmpty(request.getParameter(AUTHC_CODE_PARAM))) {
            // 如果用户没有身份验证, 且没有 auth code, 则重定向到登录页面.
            saveRequestAndRedirectToLogin(request, response);
            return false;
        }

        // 执行登录操作.
        return executeLogin(request, response);
    }

    // 当登录成功, 跳转到  shiro 配置的 successUrl.
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
                                     ServletResponse response) {
        WebHelper.redirectUrl("/oauth2/success");
        return false;
    }

}