package com.steerinfo.auth.shiro.realm;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import com.steerinfo.framework.cache.Caches;
import com.steerinfo.framework.constant.ShiroConstant;
import com.steerinfo.framework.exception.ShiroJwtException;
import com.steerinfo.framework.user.IUser;
import com.steerinfo.framework.user.UserPayload;
import com.steerinfo.framework.utils.jwt.JwtUtils;
import com.steerinfo.framework.utils.text.MD5Utils;

import io.jsonwebtoken.Claims;

import com.steerinfo.auth.shiro.token.StatelessToken;

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

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

    @Autowired
    @Lazy
    Caches caches;

    //获取用户信息
    public abstract IUser findByLogin(UserPayload payload);
        
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof StatelessToken;
    }

    /**
     * Token认证
     * 可以：
     *   对请求参数进行排序参数->自然顺序 ---- hash摘要认证
     *   有效时间之内 ---- 时间戳认证
     *   添加检查账号是否存在、是否被锁定、是否被禁用等
     *
     * 这里只检查账号是否存在
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authenticationToken) throws AuthenticationException {
        //StatelessToken对象用来存放提交的登录信息
        StatelessToken token = (StatelessToken) authenticationToken;
        //失效
        String accessToken = token.getJwt().replaceFirst(ShiroConstant.AUTH_BEARER_START, "");
        if (caches.getOneHourCache().get(ShiroConstant.JWT_ACCESS_INVALID_PRE + MD5Utils.INSTANCE.getMD5ofStr(accessToken), String.class) != null){
            logger.debug("token {}:注销失效", token.getJwt());
            throw new AuthenticationException("token失效");
        }
		// // 查看过期时间等
		// Claims claims = JwtUtils.parseJWT(accessToken);
		// System.out.println(claims.get("exp").toString());
        //取Payload
        UserPayload payload;
        try {
            payload = JwtUtils.getPayload(token.getJwt(), UserPayload.class);
        } catch (ShiroJwtException e){
            throw new AuthenticationException(e.getMessage());
        }
        if (payload == null) {
            logger.debug("token {}:主体丢失", token.getJwt());
            throw new AuthenticationException("token无效");
        }
        
        IUser user = findByLogin(payload);
        if (user == null || user.getId().isEmpty()) {
            throw new UnknownAccountException("用户不存在！");
        }
        return new SimpleAuthenticationInfo(user, token.getJwt(),getName());
    }

    /**
     * 权限认证，获取授权信息，为当前登录的Subject授予角色和权限
     * @see ：本例中该方法的调用时机为需授权资源被访问时
     * @see ：并且每次访问需授权资源时都会执行该方法中的逻辑，这表明本例中默认并未启用AuthorizationCache
     * @see ：如果连续访问同一个URL（比如刷新），该方法不会被重复调用，Shiro有一个时间间隔（也就是cache时间，在ehcache-shiro.xml中配置），超过这个时间间隔再刷新页面，该方法会被执行
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    	logger.info("这里不授权，该方法不会被调用");
        return null;
    }
}
