package com.steerinfo.auth.shiro.jwt;

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

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;

import com.steerinfo.framework.cache.Caches;
import com.steerinfo.framework.constant.ShiroConstant;
import com.steerinfo.framework.exception.ShiroJwtException;
import com.steerinfo.framework.spring.provider.ApplicationContextProvider;
import com.steerinfo.framework.user.UserPayload;
import com.steerinfo.framework.utils.jwt.JwtUtils;
import com.steerinfo.framework.utils.text.MD5Utils;
import com.steerinfo.framework.utils.web.HttpUtils;
import com.steerinfo.auth.shiro.token.StatelessToken;

/**
 * 
 * @Title: JwtStatelessAccessControlFilter.java
 * @Package com.steer.framework.auth.shiro.jwt
 * @Description: TODO
 * @author 鄢利
 * @date 2018年7月16日
 * @Copyright © 2018湖南视拓信息技术股份有限公司. All rights reserved.
 */
public class JwtStatelessAccessControlFilter extends AccessControlFilter {

   private static final Logger logger = LoggerFactory.getLogger(JwtStatelessAccessControlFilter.class);

    /**
     * 先执行：isAccessAllowed 再执行onAccessDenied
     *
     * isAccessAllowed：表示是否允许访问；mappedValue就是[urls]配置中拦截器参数部分，
     * 如果允许访问返回true，否则false；
     *
     * 如果返回true的话，就直接返回交给下一个filter进行处理。
     * 如果返回false的话，回往下执行onAccessDenied
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        logger.debug("StatelessAuthcFilter.isAccessAllowed()");
        if (mappedValue != null && ((String[])mappedValue)[0].equals("anon")){
            return true;
        }
        
        /*
        //如果有其它认证则不需进行token认证，无状态服务没有其它认证
        if (null != getSubject(request, response) && getSubject(request, response).isAuthenticated()) {
        	//已经认证过直接放行
            return true;
        }
        */
        
        return false;
    }

    /**
     * onAccessDenied：表示当访问拒绝时是否已经处理了；如果返回true表示需要继续处理；
     * 如果返回false表示该拦截器实例已经处理了，将直接返回即可。
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        logger.debug("StatelessAuthcFilter.onAccessDenied()");
        try {
            // 1、获取header里的Token
            String jwt = ((HttpServletRequest)request).getHeader(ShiroConstant.HEADERS_AUTHORIZATION);
            logger.debug(jwt);
            // header里没有，取cookies
            if (jwt == null){
                jwt = HttpUtils.getCookie(ShiroConstant.HEADERS_AUTHORIZATION, (HttpServletRequest) request);
            }
            if (jwt == null){
                throw new ShiroJwtException("Token丢失");
            } else {
            	ApplicationContextProvider.choseContext(AnnotationConfigServletWebServerApplicationContext.class);
    			Caches caches = ApplicationContextProvider.getBean("caches");
				Object obj = caches.getOneHourCache()
						.get(ShiroConstant.JWT_ACCESS_INVALID_PRE
								+ MD5Utils.INSTANCE.getMD5ofStr(jwt.replaceFirst(ShiroConstant.AUTH_BEARER_START, "")),
								String.class);
				if(obj != null) {
					if(((String)obj).equals(jwt)) {
						throw new ShiroJwtException("Token丢失");
					}
				}
                if (!jwt.startsWith(ShiroConstant.AUTH_BEARER_START)) {
                    throw new ShiroJwtException("Token无效");
                }
                jwt = jwt.replaceFirst(ShiroConstant.AUTH_BEARER_START, "");
            }
            // token失效
            try {
                JwtUtils.getPayload(jwt, UserPayload.class);
            } catch (ShiroJwtException e){
                throw new AuthenticationException(e.getMessage());
            }
            // 2、生成无状态Token
            StatelessToken token = new StatelessToken(jwt, request.getParameterMap());
            // 3、委托给Realm进行登录
            getSubject(request, response).login(token);
        } catch (UnknownAccountException e){
            logger.debug(e.getMessage());
            return false;
        }
/*        } catch (Exception e) {
            logger.debug(e.getMessage());
            // 就直接返回给请求者.
            return false;
        }*/
        return true;
    }
}
