《Java实战开发》基于JWT实现简单信息分享

公司有个合作伙伴替用户下单的需求,下单完毕需要将单据信息分享给客户看,让客户付款。由于只是一些简单的单据信息,并且没有权限校验,综合考虑安全与性能,决定使用JWT的方式实现。

JWT简介

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。具体概念自行搜索
官网介绍如下:
https://jwt.io/introduction

主要代码实现

JWT了解原理之后可以自己实现,不过现在有很多开源的JWT库,官网推荐如下:https://jwt.io/libraries 本文用的是jose4j ,如下:https://bitbucket.org/b_c/jose4j/wiki/Home

引入jar包:

  <dependency>
    <groupId>org.bitbucket.b_c</groupId>
    <artifactId>jose4j</artifactId>
    <version>0.7.9</version>
  </dependency>

这里使用枚举实现单例创建一个密钥对象,这里是我写的练练手,可以不需要,直接本地运行生成key对象直接保存

public enum SingletonRsaJsonWebKeyEnum {
    //创建一个枚举对象,该对象天生为单例
    INSTANCE;

    private RsaJsonWebKey generateJwk() {
        RsaJsonWebKey rsaJsonWebKey = null;

        try {
            rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
            rsaJsonWebKey.setKeyId("www_key_customer_pay");
        } catch (JoseException e) {
            e.printStackTrace();
        }
        return rsaJsonWebKey;
    }

    public String getRsaJsonWebKeyJsonString() {
        String keyString =        "{\"kty\":\"RSA\",\"kid\":\"www_key_customer_pay\",\"n\":\"lZJmKh4hM5_B0Cw2zUajuXhi6g-ZiWoNKAYIkC4upx8B9PtE541lwu6qYYoS_gP5K1tOTgJodwK95j8kfmq2Nevr8hr9FuROic0xftOJTJrkwsun22X73d5IhvG01t0NB-S_WmYF-JRcmJ6zQD1nnFgMTHn3zhEJrxByrsad-yGb0cdzFX8R4Ae383c8dL2VWyJRTOMAqW_fuR3TpSdFd-fYmjadmLIdodsWPchR88ZeEURYLjPgYAVbtBmfe8npIWHNKyPgnuy0JWdR7FQQFXvU5c-f4wccErB_o5oGHt4Nm8-S5cC3T0yp9e1CKaSiUOTCvwFfq5cX4Rf8WDzNyQ\",\"e\":\"AQAB\",\"d\":\"VvOTxvpbDNrb3jxF45IkTgcpYa6N8G-hlFnlkoP2hRsmlReZ2A7AUNFUZX3SSq9epBDhdcI6nq0OXpLokUFSCgjL0qRT64HwEnYiRvuMvubxBDlGrOodwL6fZSuQmLRLBgK6J0BWSktdhTAFPtwzppUdGTxyje4jtg0WznoSj1H3Gx6XN98_Nut2r5hXqP1f1nsdsE1dCaTYIJnf4kTpRBoEMjLQ2qoFr77e-wSmIrc4HFOaVzJCN4NQZjx_Vi3CdV33QX3qWEypfihEC25yGH4k7MLaP7ZSzqwv2VcvB5O7DAImYrJwQSSofiNE5yXqL6g8_0zYe91ardZngy6KzQ\",\"p\":\"1UWnxrs4FdLzfi2060zfXKiEDqAh2-XLaEtlT3fUOe3Acxb0PTzRmQICwttd1pqAvqwljdapbkOHIBiGSOeQWJQEsCVxymaUVOtWxWImZmREJh8L2KoPfUIHENOmx76FmczOtTpQi4cvUnzVy45iIshC28zePTYX7l7UdxHufmM\",\"q\":\"s4msVx1gdrYiCVv58db3jfXaKYWDYI8-8Fp_KdyKCFPmayYYeWl9wUEouywLqguwKKyRo7h2lKMEcgmQp0DniZVZpFpzxBu9rkmpbsS2xHL2FDt5cueNS_xgvVS3okmrW6rz6_ufBnjXKSGRMn8ZqZs6jDDdAZ-fD9iuOmCBFOM\",\"dp\":\"JnmFdf2qdY1z7exy-gwJM58XC8hps1D2bB9F7JsyhyzUDi8y6qVBLrhFJUAL4r5GwZ8uuzLhqAm4o9qoxxg3WzOA0QJAUAJHejZmlf9J7CjkfngVXAX5-1_hBHEaYmiFk6R2Gg2mIDXIHLp2m11ZaOr13M9NvH84vAERlVV_z_8\",\"dq\":\"C9ncnGt-AJgJKyD9IK-V67L80ZzUjT0nZGo01qiOG_qdzRjYqLsD6AvLCn_fzTu0RxsNCgeVHD6efoCPIdsw7W4EWsr1tU43eRe3rW4iulj0UWWToLgUJZ3lLNo4vOer_gMM0tjrKFw1p6tlkDW_leh_Nt3K2N6We09MQOI54w0\",\"qi\":\"g66xqKV_Ci2gVgylelYk7FbypqhRsUDBrbqBZUaEK0nIW3HZGsqKB1g55vO60R4_lhftFTwrkabBtGipt6fRk3GPywj1usp3AVtR0uvBNPHGAiTYJ0hEd98uiwMQ2MteZuj3Iy-RIs9BHlN2QJVTgfZ_ulgO_viMQlfQNxrBfcA\"}\n"
;
//        if (keyString == null) {
//            keyString = SingletonRsaJsonWebKeyEnum.INSTANCE.generateJwk().toJson(RsaJsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
//        }
        return keyString;
    }

//    public static void main(String[] args) {
//
//        String keyString = SingletonRsaJsonWebKeyEnum.INSTANCE.generateJwk().toJson(RsaJsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
//        RsaJsonWebKey  rsaJsonWebKey = new RsaJsonWebKey(JsonUtil.parseJson(SingletonRsaJsonWebKeyEnum.INSTANCE.getRsaJsonWebKeyJsonString()));
//
//    }
}

工具类:

@Slf4j
public class JwtUtils {
    public static RsaJsonWebKey rsaJsonWebKey;

    static {
        try {
            rsaJsonWebKey = new RsaJsonWebKey(JsonUtil.parseJson(SingletonRsaJsonWebKeyEnum.INSTANCE.getRsaJsonWebKeyJsonString()));
        } catch (JoseException e) {
            e.printStackTrace();
        }
    }

    private JwtUtils() {
    }

    public static String createToken(Object jsonString) throws JoseException {
        //
        // JSON Web Token is a compact URL-safe means of representing claims/attributes to be transferred between two parties.
        // This example demonstrates producing and consuming a signed JWT
        //
//        RsaJsonWebKey rsaJsonWebKey = SingletonRsaJsonWebKeyEnum.generateJwk();

        // Give the JWK a Key ID (kid), which is just the polite thing to do


        // Create the Claims, which will be the content of the JWT
        JwtClaims claims = new JwtClaims();
        claims.setIssuer("WWW");  // who creates the token and signs it
        claims.setAudience("WWW_CUSTOMER"); // to whom the token is intended to be sent
        claims.setExpirationTimeMinutesInTheFuture(48 * 60); // time when the token will expire ( minutes from now)
        claims.setGeneratedJwtId(); // a unique identifier for the token
        claims.setIssuedAtToNow();  // when the token was issued/created (now)
        claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
        claims.setSubject("测试Token"); // the subject/principal is whom the token is about
        claims.setClaim("data", jsonString); // additional claims/attributes about the subject can be added
//        List<String> groups = Arrays.asList("group-one", "other-group", "group-three");
//        claims.setStringListClaim("groups", groups); // multi-valued claims work too and will end up as a JSON array

        // A JWT is a JWS and/or a JWE with JSON claims as the payload.
        // In this example it is a JWS so we create a JsonWebSignature object.
        JsonWebSignature jws = new JsonWebSignature();

        // The payload of the JWS is JSON content of the JWT Claims
        jws.setPayload(claims.toJson());

        // The JWT is signed using the private key
        jws.setKey(rsaJsonWebKey.getPrivateKey());

        // Set the Key ID (kid) header because it's just the polite thing to do.
        // We only have one key in this example but a using a Key ID helps
        // facilitate a smooth key rollover process
        jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());

        // Set the signature algorithm on the JWT/JWS that will integrity protect the claims
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);

        // Sign the JWS and produce the compact serialization or the complete JWT/JWS
        // representation, which is a string consisting of three dot ('.') separated
        // base64url-encoded parts in the form Header.Payload.Signature
        // If you wanted to encrypt it, you can simply set this jwt as the payload
        // of a JsonWebEncryption object and set the cty (Content Type) header to "jwt".
        String jwt = jws.getCompactSerialization();


        // Now you can do something with the JWT. Like send it to some other party
        // over the clouds and through the interwebs.
        return jwt;

    }


    public static JwtClaims validateToken(String jwt)  {

//        RsaJsonWebKey rsaJsonWebKey = SingletonRsaJsonWebKeyEnum.generateJwk();
        // Use JwtConsumerBuilder to construct an appropriate JwtConsumer, which will
        // be used to validate and process the JWT.
        // The specific validation requirements for a JWT are context dependent, however,
        // it typically advisable to require a (reasonable) expiration time, a trusted issuer, and
        // and audience that identifies your system as the intended recipient.
        // If the JWT is encrypted too, you need only provide a decryption key or
        // decryption key resolver to the builder.
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime() // the JWT must have an expiration time
                .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
                .setRequireSubject() // the JWT must have a subject claim
                .setExpectedIssuer("WWW") // whom the JWT needs to have been issued by
                .setExpectedAudience("WWW_CUSTOMER") // to whom the JWT is intended for
                .setVerificationKey(rsaJsonWebKey.getKey()) // verify the signature with the public key
                .setJwsAlgorithmConstraints( // only allow the expected signature algorithm(s) in the given context
                        AlgorithmConstraints.ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256) // which is only RS256 here
                .build(); // create the JwtConsumer instance
        JwtClaims jwtClaims = null;
        try {
            //  Validate the JWT and process it to the Claims
            jwtClaims = jwtConsumer.processToClaims(jwt);
            log.info("JWT validation succeeded! " + jwtClaims);
        } catch (InvalidJwtException e) {
            // InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
            // Hopefully with meaningful explanations(s) about what went wrong.
            log.error("Invalid JWT! " + e);

            // Programmatic access to (some) specific reasons for JWT invalidity is also possible
            // should you want different error handling behavior for certain conditions.

            // Whether or not the JWT has expired being one common reason for invalidity
            try {
                if (e.hasExpired()) {
                    log.error("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
                }

                // Or maybe the audience was invalid
                if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) {
                    log.error("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
                }
            }catch (MalformedClaimException  e1){
                log.error("验证客户支付token异常",e1);
            }

            throw new MallBusinessException(StatusEnum.CUSTOMER_PAY_TOKEN_AUTHENTICATION_ERROR);
        }

        return jwtClaims;
    }

 

    public static void main(String[] args) throws Exception {
        String json = "{\"name\":\"小二\",\"age\":\"18\"}";
        String jwt = createToken(json);
//        String jwt = "";
        JwtClaims result = validateToken(jwt);
        System.out.println("开始:" + jwt);
        System.out.println("结果:" + result.getClaimValue("data"));
        System.out.println("结果:" + result);
        System.out.println("结果:" + result.getExpirationTime());
    }




}
### 测试结果

开始:eyJraWQiOiJ5empfa2V5X2N1c3RvbWVyX3BheSIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJXV1ciLCJhdWQiOiJXV1dfQ1VTVE9NRVIiLCJleHAiOjE2MzQ5Nzk2ODIsImp0aSI6ImVMVXZpYU8wUWUzR2paRy13LVlGc1EiLCJpYXQiOjE2MzQ4MDY4ODMsIm5iZiI6MTYzNDgwNjc2Mywic3ViIjoi5rWL6K-VVG9rZW4iLCJkYXRhIjoie1wibmFtZVwiOlwi5bCP5LqMXCIsXCJhZ2VcIjpcIjE4XCJ9In0.NOyEWUpFZteA5z44oUZ0_EsvW1bN73Uk4S8OqFWKMUOOfgM1t68xoDjiEnhPUnORLEdCPQBK5B_F-qiPu8uaNfS23B2Xu1hNwS_2Nig5TUO9R_izZY-ECGjQW8G7u-8dJTfGkgpnHTC3IEzdhc_pnlNxWCtUfiwrDnuk2Kzy1eXNjZDevYE0dkRaTh6s-5J58hyS3MshgLPZ2YSJpjWYZgSJObNzoypjVgasLFyx1Jx45Sb4YWEuKi888xzHQZNdksYigCDubsyy-LJ7b75wx5XtdWKto0Xk_s8Cby38zV64VxTS51zQIxcNay6VMgv3qn0TkcbQNYZg6BSXfVlWFw
结果:{“name”:“小二”,“age”:“18”}
结果:JWT Claims Set:{iss=WWW, aud=WWW_CUSTOMER, exp=1634979682, jti=eLUviaO0Qe3GjZG-w-YFsQ, iat=1634806883, nbf=1634806763, sub=测试Token, data={“name”:“小二”,“age”:“18”}}
结果:NumericDate{1634979682 -> 2021-10-23 下午05时01分22秒}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值