JAVA造轮子之-JWT跨域管理token

网上关于jwt创建token的资料很多,有讲原理的,也有深度解析的,也存在很多的示例,但大部份示例,把简单的方式搞的复杂化,因此稍做整理提供一个可用的工具类;

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。
JWT 的三个部分依次如下
Header(头部):是一个 JSON 对象,描述 JWT 的元数据{ "alg": "HS256", typ": "JWT" }
Payload(负载):也是一个 JSON 对象,用来存放实际需要传递的数据,JWT 规定了7个官方字段:
    iss (issuer):签发人
    exp (expiration time):过期时间
    sub (subject):主题
    aud (audience):受众
    nbf (Not Before):生效时间
    iat (Issued At):签发时间
    jti (JWT ID):编号
Signature(签名):对前两部分的签名,防止数据篡改

1.JWT中Header头和Payload有效载荷序列化的算法都用到了Base64URL,签名哈希部分是对Header与Payload两部分数据签名
2.客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中,客户端将在与服务器交互中都会带JWT,将它放入HTTP请求的Header Authorization字段中
3.JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限
4.JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限
5.JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输

pom.xml引入依赖包

<!-- JWT Token验证机制 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

工具类


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import org.apache.commons.lang3.time.DateUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description JWT跨域管理token工具类
 * @Author JL
 * @Date 2019/08/08
 * @Version V1.0
 */
public class JwtTokenUtils {


    /**
     * 加密密钥
     */
    private static final String SECRET = "wNmx01w27MQnPc3BtUQkty_23P0pVlAdj86o5XznUrE";

    /**
     * jwt创建token,考虑安全性,token中不因该放入太多信息(勿放密码之类的敏感信息),只放入关键字段值即可,如用户ID
     * @param sub     主题(可以放入关键数据,如:userid, 用户唯一值等)
     * @param timeout 过期时长(秒)
     * @return
     */
    public static String createToken(String sub, int timeout) {
        JWTCreator.Builder builder = JWT.create();
        builder.withSubject(sub);//主题
        builder.withIssuer("pro-server");
        builder.withExpiresAt(DateUtils.addSeconds(new Date(), timeout));//过期时间,30秒后过期
        String jwtToken = builder.sign(Algorithm.HMAC256(SECRET));
        return jwtToken;
    }

    /**
     * 对jwt创建的token进行验签与解析,返回Subject(主题)中存放的内容
     * @param token
     * @return
     * @throws TokenExpiredException          会话超时异常
     * @throws SignatureVerificationException 验签无效异常
     */
    public static String parseToken(String token) throws TokenExpiredException, SignatureVerificationException {
        return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token).getSubject();
    }

    /**
     * jwt创建token,考虑安全性,token中不因该放入太多信息(勿放密码之类的敏感信息)
     * @param loadMap   数据集合
     * @param timeout   过期时长(秒)
     * @return
     */
    public static String createToken(Map<String, Object> loadMap, int timeout) {
        JWTCreator.Builder builder = JWT.create();
        loadMap.forEach((k, v) -> {
            if (v instanceof String) {
                builder.withClaim(k, (String) v);
            } else if (v instanceof Date) {
                builder.withClaim(k, (Date) v);
            } else if (v instanceof Long) {
                builder.withClaim(k, (Long) v);
            } else if (v instanceof Integer) {
                builder.withClaim(k, (Integer) v);
            } else if (v instanceof Boolean) {
                builder.withClaim(k, (Boolean) v);
            }
        });
        builder.withIssuer("pro-server");
        builder.withExpiresAt(DateUtils.addSeconds(new Date(), timeout));//过期时间,30秒后过期
        String jwtToken = builder.sign(Algorithm.HMAC256(SECRET));
        return jwtToken;
    }

    /**
     * 对jwt创建的token进行验签与解析,返回集合
     * @param token
     * @return
     * @throws TokenExpiredException          会话超时异常
     * @throws SignatureVerificationException 验签无效异常
     */
    public static Map<String, Object> parseTokenToMap(String token) throws TokenExpiredException, SignatureVerificationException {
        Map<String, Claim> claimMap = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token).getClaims();
        if (claimMap == null){
            return null;
        }
        Map<String, Object> loadMap = new HashMap<>();
        claimMap.forEach((k, v) -> {
            Object obj = null;
            if (v.asString() != null) {
                obj = v.asString();
            } else if (v.asBoolean() != null) {
                obj = v.asBoolean();
            } else if (v.asDate() != null || v.asLong() != null) {//Date类型按Long方式来处理
                obj = v.asLong();
            } else if (v.asInt() != null) {
                obj = v.asInt();
            }
            loadMap.put(k, obj);
        });
        return loadMap;
    }

    //测试方法
    //登录成功后,将用户的id放入到JwtTokenUtils.createToken(userid, 60 * 30);
    //每次请求在拦截器或过滤器中,获取请求中的token调用JwtTokenUtils.parseToken(jwtToken)验证是否有效,或从token中获取userid进行业务逻辑操作
    public static void main(String[] args) {
        //创建token和解析token
        String subject = "userid_001";
        System.out.println("新建subject = " + subject);
        String jwtToken = JwtTokenUtils.createToken(subject, 60);
        System.out.println("生成token = " + jwtToken);
        try {
            subject = JwtTokenUtils.parseToken(jwtToken);
            System.out.println("解析subject = " + subject);
        } catch(TokenExpiredException tee){
            throw new TokenExpiredException("token已过有效期,请重新申请:" + tee.getMessage());
        } catch(SignatureVerificationException sve){
            //验证签名不通过(数据被篡改过)
            throw sve;
        }

        //过期测试
//        String jwtToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyaWRfMDAxIiwiaXNzIjoicHJvLXNlcnZlciIsImV4cCI6MTU2NTI0OTMxMH0.0cSnHLHTqDx-FXoL08yk6AtIwobiWcMNRofyE4dunGY";
//        subject = JwtTokenUtils.parseToken(jwtToken);

        //无效验鉴(将最后一位更改为z)
//        String jwtToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyaWRfMDAxIiwiaXNzIjoicHJvLXNlcnZlciIsImV4cCI6MTU2NTI0OTMxMH0.0cSnHLHTqDx-FXoL08yk6AtIwobiWcMNRofyE4dunGz";
//        subject = JwtTokenUtils.parseToken(jwtToken);


        //将多种数据放入集合中,通过jwt创建token
        /*
        Map<String, Object> loadMap = new HashMap<>();
        loadMap.put("userId", (Long) 1000000L);
        loadMap.put("userName", "test");
        loadMap.put("isLogin", true);
        String jwtToken = JwtTokenUtils.createToken(loadMap, 60);
        System.out.println(jwtToken);

        Map<String, Object> loadMap2 = JwtTokenUtils.parseTokenToMap(jwtToken);
        for (Map.Entry<String, Object> entry : loadMap2.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
        */
    }
}

请求中集成使用

1.登录成功后,将用户的id放入到JwtTokenUtils.createToken(userid, 60 * 30);
2.对需要鉴权的每次URL请求在拦截器或过滤器中处理,获取请求中的token(一般放在请求头部,每次请求必带)调用JwtTokenUtils.parseToken(jwtToken)验证是否有效,或从token中获取userid进行业务逻辑操作

 

说明:

做过项目的人都知道,很多写过的可重复利用的代码块或有用的工具类没有怎么整理,当需要的时候,又得打开项目查找一翻,虽然功能开发不难,但是又得花时间成本去写去测试,这样的重复造轮子的事情太多次了;因此不如把轮子保留,供大家一起使用;

1.这个轮子可以有:需要使用的时候确实还不存在这个组件。
2.我需要的时候轮子不在:每一种技术或工具产生都有它的项目背景,当代码写在项目里的时候,我知道有这个东西,当换了一个项目或公司后,没有备份也没有记录,这个时候你不在了,又得花时间手打一遍;
3.我不知道是不是造轮子:大多数情况下初学者很难分清楚自己是不是在重复造轮子,事实上造轮子不是我目的。我的目的是完成工作任务,任务完成的速度越快越好,质量越高越好。而不是去判断自己在不在造轮子。
4.不想重复花时间造轮子:有时候还会碰到一些并不困难但是很占时间的东西,当然有现成的轮子是花时间最少的;
5.我就是想学习轮子:初学者的并不是在重复造轮子,而是学习后以提高为自己的知识与技能。

轮子有过测试,但难免有失误,如有错误处,还敬请指出;

转载于:https://my.oschina.net/u/437309/blog/3085385

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值