package com.fdage.shiro;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.*;

public class JwtUtil2 {

    //生成签名的时候使用的秘钥secret
    private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK&sdfkjsdrow32234545fdf>?N<:{LWPW";

    /**
     * 用户登录成功后生成Jwt
     * 使用Hs256算法  私匙使用用户密码
     *
     * @param ttlMillis jwt过期时间
     * @param userName  用户名
     * @return
     */
    public static String createJWT(long ttlMillis, String userName) {
        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("userName", userName);

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                //iat: jwt的签发时间
                .setIssuedAt(now)
                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(userName)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, SECRET);

        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(exp);
        }/*else{
            //设置过期时间半小时
            DateTime expDate = new DateTime().plusMinutes(30);
            builder.setExpiration(expDate.toDate());
        }*/
        return builder.compact();
    }


    /**
     * Token的解密
     * @param token 加密后的token
     * @return
     */
    public static Claims parseJWT(String token) {
        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //设置签名的秘钥
                .setSigningKey(SECRET)
                //设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }


    /**
     * 校验token
     * 在这里可以使用官方的校验，我这里校验的是token中携带的密码于数据库一致的话就校验通过
     * @param token
     * @param userName
     * @return
     */
    public static Boolean isVerify(String token, String userName) {

        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //设置签名的秘钥
                .setSigningKey(SECRET)
                //设置需要解析的jwt
                .parseClaimsJws(token).getBody();

        if (claims.get("userName").equals(userName)) {
            return true;
        }
        return false;
    }

    /**
     * 获得token中的信息无需secret解密也能获得
     *
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("userName").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }



    public static List getUserRole(String token) {
//        try {
            DecodedJWT jwt = JWT.decode(token);
            Claim role = jwt.getClaim("role");
            return role.as(List.class);
//        } catch (JWTDecodeException e) {
//            return null;
//        }
    }


    public static String createJWT(long ttlMillis, Map<String, Object> paramMap) {
        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        String userName = paramMap.get("userName").toString();

        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("userName", userName);
        claims.put("id", paramMap.get("id"));
        claims.put("role", paramMap.get("role"));

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                //iat: jwt的签发时间
                .setIssuedAt(now)
                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(userName)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, SECRET);

        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(exp);
        }/*else{
            //设置过期时间半小时
            DateTime expDate = new DateTime().plusMinutes(30);
            builder.setExpiration(expDate.toDate());
        }*/
        return builder.compact();
    }

    public static Long getUserId(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            Claim id = jwt.getClaim("id");
            return id.asLong();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    public static void main(String[] args) {
//        test3();
//        test2();
        long nowMillis = System.currentTimeMillis();
        System.out.println(nowMillis);
        Date exp = new Date(nowMillis);
        System.out.println(exp);
    }

    public static void test2(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOlsiYWRtaW4iLCJyb290Il0sImlkIjoxLCJ1c2VyTmFtZSI6ImFkbWluIiwiaWF0IjoxNTgzODA5MzkzLCJqdGkiOiJkNjZiZTFkYi00MTQ0LTQxMTYtYTNlNi01ZDBjNjhlNTI3ODAifQ.-4AdsVP2RwmPS2grtO4aC8ov9PwkilzaGdThGetBJok";
        System.out.println(getUserRole(token));

    }

    public static void test3(){
        HashMap<String, Object> map = new HashMap<>();

        ArrayList<Object> list = new ArrayList<>();
        list.add("admin");
        list.add("root");

        map.put("userName", "admin");
        map.put("id", Long.valueOf("1"));
        map.put("role", list);

        String token = createJWT(-1, map);
        System.out.println(token);

        Boolean isVerify = isVerify(token, "admin");
        System.out.println(isVerify);

    }

    private static void testList(){
        ArrayList<Object> list = new ArrayList<>();
        list.add("admin");
        list.add("root");
    }

}
