package com.cftech.core.util;

import com.alibaba.fastjson.JSONObject;
import com.cftech.core.config.MpGlobalConfig;
import com.cftech.core.jwt.JWTStatus;
import io.jsonwebtoken.*;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.net.URL;
import java.util.Date;
import java.util.Map;

/**
 * JWT 生成器
 * Created by Jasper Huang on 2018/6/4.
 */
public class JWTUtils {

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

    //密钥
    public static final String JWT_SECRETKEY = "3d6dffa1c9364da088ea9a8bd4d74933";
    //密钥BASE 64编码后，转成的加密解密密钥
    public static final SecretKey sk;
    //
    public static final long DURATION = MpGlobalConfig.JWT_DURATION;


    static
    {
        String stringKey = JWT_SECRETKEY;// Constant.JWT_SECRET;//本地配置文件中加密的密文7786df7fc3a34e26a61c034d5ec8245d
        byte[] encodedKey = Base64.decodeBase64(stringKey);//本地的密码解码[B@152f6e2
        sk = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// 根据给定的字节数组使用AES加密算法构造一个密钥，使用 encodedKey中的始于且包含 0 到前 leng 个字节这是当然是所有。（后面的文章中马上回推出讲解Java加密和解密的一些算法）
    }


    /**
     * 创建jwt
     * @param id
     * @param subject
     * @param ttlMillis 过期的时间长度
     * @return
     * @throws Exception
     */
    public static String createJWT(String id,WxLoginEntity wle)  {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        long nowMillis = System.currentTimeMillis();//生成JWT的时间
        Date now = new Date(nowMillis);
        Map<String,Object> claims = wle.claims(); //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        SecretKey key = generalKey();//生成签名的时候使用的秘钥secret,这个方法本地封装了的，一般可以从本地配置文件中读取，切记这个秘钥不能外露哦。它就是你服务端的私钥，在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
        //下面就是在为payload添加各种标准声明和私有声明了
        JwtBuilder builder = Jwts.builder() //这里其实就是new一个JwtBuilder，设置jwt的body
                .setClaims(claims)          //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setId(id)                  //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(now)           //iat: jwt的签发时间
                .setIssuer(MpGlobalConfig.JWT_DOMAIN)
                .setAudience(wle.getOpenId())
                .setSubject(wle.subject())        //sub(Subject)：代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setHeaderParam("typ","JWT").setHeaderParam("alg","HS256")
                .signWith(signatureAlgorithm, key);//设置签名使用的签名算法和签名使用的秘钥
//        if (ttlMillis >= 0) {
            long expMillis = nowMillis + DURATION;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);     //设置过期时间
//        }
        return builder.compact();           //就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
    }



    /**
     * 解密jwt
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) {
        SecretKey key = generalKey();  //签名秘钥，和生成的签名的秘钥一模一样
        Claims claims = Jwts.parser()  //得到DefaultJwtParser
                .setSigningKey(key)         //设置签名的秘钥
                .parseClaimsJws(jwt).getBody();//设置需要解析的jwt
        return claims;
    }

    /**
     * 校验jwt requestId, openId 必传
     * @param jwt
     * @return
     * @throws Exception
     */
    public static int verifyJWT(String signature, HttpServletRequest request) {
        SecretKey key = generalKey();  //签名秘钥，和生成的签名的秘钥一模一样
        try {
            String openId = request.getParameter("openId");
            String requestId = request.getParameter("requestId");
            String memberUid = request.getParameter("memberUid");
            if (memberUid == null) {
                memberUid = "";
            }
            String refer = request.getHeader("Referer");
            if (StringUtils.isBlank(refer))
            {
                return JWTStatus.CROSSDOMAIN.getCode();
            }
            URL url = new URL(refer);
            String remoteHost = url.getHost();

//            不校验POST 请求的origin字段
//            String origin = request.getHeader("Origin");
//            if(StringUtils.isNotBlank(origin))
//            { //如果是AJAX  POST请求，一定会带上Origin字段，此时则用Origin进行判断
//                URL originUrl = new URL(origin);
//                remoteHost = originUrl.getHost();
//            }

            JSONObject subject = new JSONObject();
            subject.put("openId",openId);
            subject.put("memberUid",memberUid);
            Jwts.parser()  //得到DefaultJwtParser
                    .setSigningKey(key)         //设置签名的秘钥
                    .requireIssuer(remoteHost)
                    .requireId(requestId)
                    .requireAudience(openId)
                    .requireSubject(subject.toJSONString())
                    .parseClaimsJws(signature).getBody();//设置需要解析的jwt
        }catch (ExpiredJwtException e)
        {
            logger.error(JWTStatus.ExpiredJwtException.getDes(),e);
            return JWTStatus.ExpiredJwtException.getCode();
        }catch (UnsupportedJwtException e)
        {
            logger.error(JWTStatus.UnsupportedJwtException.getDes(),e);
            return JWTStatus.UnsupportedJwtException.getCode();
        }
        catch (MalformedJwtException e)
        {
            logger.error(JWTStatus.MalformedJwtException.getDes(),e);
            return JWTStatus.MalformedJwtException.getCode();
        }
        catch (SignatureException e)
        {
            logger.error(JWTStatus.SignatureException.getDes(),e);
            return JWTStatus.SignatureException.getCode();
        }
        catch (IllegalArgumentException e)
        {
            logger.error(JWTStatus.IllegalArgumentException.getDes(),e);
            return JWTStatus.IllegalArgumentException.getCode();
        }
        catch (Exception e)
        {
            logger.error(JWTStatus.Exception.getDes(),e);
            return JWTStatus.Exception.getCode();
        }
        return JWTStatus.OK.getCode();
    }

    /**
     * 由字符串生成加密key
     * @return
     */
    public static SecretKey generalKey(){
//        String stringKey = JWT_SECRETKEY;// Constant.JWT_SECRET;//本地配置文件中加密的密文7786df7fc3a34e26a61c034d5ec8245d
//        byte[] encodedKey = Base64.decodeBase64(stringKey);//本地的密码解码[B@152f6e2
////        System.out.println(encodedKey);//[B@152f6e2
////        System.out.println(Base64.encodeBase64URLSafeString(encodedKey));//7786df7fc3a34e26a61c034d5ec8245d
//        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// 根据给定的字节数组使用AES加密算法构造一个密钥，使用 encodedKey中的始于且包含 0 到前 leng 个字节这是当然是所有。（后面的文章中马上回推出讲解Java加密和解密的一些算法）
//        return key;
            return sk;
    }
    public static void main(String[] args) throws Exception {
//        String ab= createJWT("jwt", "KK","{memberid:100,openid:opnehsk}", 1);
//        System.out.println(ab);
//        //eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJEU1NGQVdEV0FEQVMuLi4iLCJzdWIiOiJ7aWQ6MTAwLG5hbWU6eGlhb2hvbmd9IiwidXNlcl9uYW1lIjoiYWRtaW4iLCJuaWNrX25hbWUiOiJEQVNEQTEyMSIsImV4cCI6MTUxNzgzNTE0NiwiaWF0IjoxNTE3ODM1MDg2LCJqdGkiOiJqd3QifQ.ncVrqdXeiCfrB9v6BulDRWUDDdROB7f-_Hg5N0po980
//        //String jwt="eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJEU1NGQVdEV0FEQVMuLi4iLCJzdWIiOiJ7aWQ6MTAwLG5hbWU6eGlhb2hvbmd9IiwidXNlcl9uYW1lIjoiYWRtaW4iLCJuaWNrX25hbWUiOiJEQVNEQTEyMSIsImV4cCI6MTUxNzgzNTEwOSwiaWF0IjoxNTE3ODM1MDQ5LCJqdGkiOiJqd3QifQ.G_ovXAVTlB4WcyD693VxRRjOxa4W5Z-fklOp_iHj3Fg";
//        Claims c= parseJWT(ab);//注意：如果jwt已经过期了，这里会抛出jwt过期异常。
//        System.out.println(c.getId());//jwt
//        System.out.println(c.getIssuedAt());//Mon Feb 05 20:50:49 CST 2018
//        System.out.println(c.getSubject());//{id:100,name:xiaohong}
//        System.out.println(c.getIssuer());//null
//        System.out.println(c.get("uid", String.class));//DSSFAWDWADAS...
//
//        System.out.println(Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(ab).getHeader().get("typ"));
    }
}
