精华内容
下载资源
问答
  • JAVA 实现 JWT

    千次阅读 2020-08-20 11:38:56
    引入JWT依赖,由于是基于Java,所以需要的是java-jwt <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> <...
    1. 引入JWT依赖,由于是基于Java,所以需要的是java-jwt
    <dependency>
          <groupId>com.auth0</groupId>
          <artifactId>java-jwt</artifactId>
          <version>3.4.0</version>
    </dependency>
    
    1. 自定义注解用于判断是否需要验证
    • 用来跳过验证的PassToken
     @Target({ElementType.METHOD, ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface PassToken {
          boolean required() default true;
      }
    
    • 需要登录才能进行操作的注解UserLoginToken
    @Target({ElementType.METHOD, ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          public @interface UserLoginToken {
              boolean required() default true;
          }
    

    @Target:注解的作用目标

    • @Target(ElementType.TYPE)——接口、类、枚举、注解
    • @Target(ElementType.FIELD)——字段、枚举的常量
    • @Target(ElementType.METHOD)——方法
    • @Target(ElementType.PARAMETER)——方法参数
    • @Target(ElementType.CONSTRUCTOR) ——构造函数
    • @Target(ElementType.LOCAL_VARIABLE)——局部变量
    • @Target(ElementType.ANNOTATION_TYPE)——注解
    • @Target(ElementType.PACKAGE)——包
      @Retention:注解的保留位置
      RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。 RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。 RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
      @Document:说明该注解将被包含在javadoc中
      @Inherited:说明子类可以继承父类中的该注解

    3.编写token的生成方法

    • 生成不携带自定义信息的 JWT token
    1. 构建头部信息
     Map<String, Object> map = new HashMap<String, Object>();
     map.put("alg", "HS256");
     map.put("typ", "JWT");
    
    1. 构建密钥信息
     Algorithm algorithm = Algorithm.HMAC256("secret");
    

    Algorithm.HMAC256(): 使 HS256 生token,唯一密钥可以保存在服务端。“secret” 为相应的密钥

    1. 我们通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token
     String token = JWT.create()
         .withHeader(map)// 设置头部信息 Header 
         .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
         .withSubject("this is test token")//设置 载荷 签名的主题
         // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
         .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
         .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
         .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
         .sign(algorithm);//签名 Signature
    
    • 生成携带自定义信息的 JWT token
      自定义信息通过 withClaim 方法进行添加,具体操作如下:
    JWT.create()
          .withHeader(map)
          .withClaim("key", "value")
    
    1. 验证 JWT token
    • 构建密钥信息
      Algorithm algorithm = Algorithm.HMAC256("secret");
    
    • 通过密钥信息和签名的发布者的信息生成 JWTVerifier (JWT验证类)
     JWTVerifier verifier = JWT.require(algorithm)
       		        .withIssuer("SERVICE")
       		        .build();
    

    不添加 .withIssuer(“SERVICE”) 也是可以获取 JWTVerifier 。

    • 通过 JWTVerifier 的verify获取 token中的信息。
    DecodedJWT jwt = verifier.verify(token);
    

    如下面代码所示就可以获取到我们之前生成 token 的 签名的主题,观众 和自定义的声明信息。

     String subject = jwt.getSubject();//获得签名主题
       List<String> audience = jwt.getAudience();//获得签名接收方
       Map<String, Claim> claims = jwt.getClaims();//获得自定义信息
       for (Entry<String, Claim> entry : claims.entrySet()) {
           String key = entry.getKey();
       	Claim claim = entry.getValue();
       	System.out.println("key:"+key+" value:"+claim.asString());
       }
    
    1. 编写拦截器 interceptor
    public class AuthenticationInterceptor implements HandlerInterceptor {
        @Autowired
        UserService userService;
    
        @Override
        public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object object) throws IOException {
            String token = req.getHeader("token");// 从 http 请求头中取出 token
            // 如果不是映射到方法直接通过
            if(!(object instanceof HandlerMethod)){
                return true;
            }
            //获得控制器函数所在的控制器类
            HandlerMethod handlerMethod = (HandlerMethod)object;
            //获得控制器函数
            Method method = handlerMethod.getMethod();
            //获得控制器函数所在的控制器类的Class
            Class controllerClass = handlerMethod.getBean().getClass();
    
            //检查类中是否有跳过认证的注解
            if(controllerClass.isAnnotationPresent(PassToken.class)){
                PassToken passToken = (PassToken) controllerClass.getAnnotation(PassToken.class);
                if (passToken.required()) {
                    return true;
                }
            }
    
            //检查方法是否有passtoken注释,有则跳过认证
            if (method.isAnnotationPresent(PassToken.class)) {
                PassToken passToken = method.getAnnotation(PassToken.class);
                if (passToken.required()) {
                    return true;
                }
            }
    
            //判断类中是否有登陆注解
            if(controllerClass.isAnnotationPresent(UserLoginToken.class)){
                UserLoginToken userLoginToken = (UserLoginToken);
                controllerClass.getAnnotation(UserLoginToken.class);
                //验证token
                needUserLogin(userLoginToken, token);
            }
    
            //判断方法中是否有登陆的注解
            if(method.isAnnotationPresent(UserLoginToken.class)){
                UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
                //验证token
                needUserLogin(userLoginToken, token);
            }
            return true;
        }
    	
    	//验证注解的函数
        public boolean needUserLogin(UserLoginToken userLoginToken, String token){
        
            if(userLoginToken.required()){
            	//需要验证
                if(token == null){
                    throw new RuntimeException("无token, 请重新登陆");
                }
                
                String un;
                try{
                	//获取签名接受方
                    un = JWT.decode(token).getAudience().get(0);
                    
                }catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                //通过用户名获取用户信息
                User user = userService.findUserByUn(un);
                if(user == null){
                    throw new RuntimeException("用户不存在,请重新登陆");
                }
    
               	// 通过生成算法及密钥生成 token验证类
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPd())).build();
                try {
                	//验证 token,验证错误或token超时会抛出异常
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
            return true;
        }
    }
    

    实现一个拦截器就需要实现HandlerInterceptor接口

    HandlerInterceptor接口主要定义了三个方法

    • boolean preHandle (): 预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行 postHandle()和afterCompletion();false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。
    • void postHandle(): 后处理回调方法,实现处理器的后处理(DispatcherServlet进行视图返回渲染之前进行调用),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
    • void afterCompletion(): 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值为true时才会执行,也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
      主要流程:
      1.从 http 请求头中取出 token
      2.判断是否映射到方法或控制器类
      3.检查是否有passtoken注释,有则跳过认证
      4.检查有没有需要用户登录的注解,有则需要取出并验证
      5.认证通过则可以访问,不通过会报相关错误信息

    6.注册拦截器

    在配置类上添加了注解@Configuration,标明了该类是一个配置类并且会将该类作为一个SpringBean添加到IOC容器内

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor())
                    .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @UserLoginToken 注解 决定是否需要登录
        }
        @Bean
        public AuthenticationInterceptor authenticationInterceptor() {
            return new AuthenticationInterceptor();
        }
    }
    

    总结

    JWT 就是一个生成 Token 的工具,如果不使用 JWT 我们也可以根据自己加密规则生成 Token。只不过 JWT 规范了生成 Token 定义了一个标准而已。JWT 的核心的功能就是:生成Token、解析Token。

    参考博客:

    • https://blog.csdn.net/ljk126wy/article/details/82751787
    • https://www.jianshu.com/p/e88d3f8151db
    展开全文
  • Java实现JWT

    2017-09-21 13:52:43
    package com.hthl.jwt; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.com
    package com.hthl.jwt;
    
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.apache.commons.codec.binary.Base64;
    
    
    public class JwtDemo {
    
    	private static final  String MAC_INSTANCE_NAME = "HMacSHA256";
    
    	public static String Hmacsha256(String secret, String message) throws NoSuchAlgorithmException, InvalidKeyException {
    	    Mac hmac_sha256 = Mac.getInstance(MAC_INSTANCE_NAME);
    	    SecretKeySpec key = new SecretKeySpec(secret.getBytes(), MAC_INSTANCE_NAME);
    	    hmac_sha256.init(key);
    	    byte[] buff = hmac_sha256.doFinal(message.getBytes());
    	    return Base64.encodeBase64URLSafeString(buff);
    	}
    
    	// java jwt
    	public static void testJWT() throws InvalidKeyException, NoSuchAlgorithmException {
    	    String secret = "eerp";
    	    String header = "{\"type\":\"JWT\",\"alg\":\"HS256\"}";
    	    String claim = "{\"iss\":\"cnooc\", \"sub\":\"yrm\", \"username\":\"yrm\", \"admin\":true}";
    
    	    String base64Header = Base64.encodeBase64URLSafeString(header.getBytes());
    	    String base64Claim = Base64.encodeBase64URLSafeString(claim.getBytes());
    	    String signature = Hmacsha256(secret, base64Header + "." + base64Claim);
    
    	    String jwt = base64Header + "." + base64Claim  + "." + signature;
    	    System.out.println(jwt);
    	}
    	
    	public static void main(String[] args) throws Exception{
    		testJWT();
    	}
    }

    展开全文
  • JWT(二):使用 Java 实现 JWT 介绍 原理在上篇《JWT(一):认识 JSON Web Token》已经说过了,实现起来并不难,你可以自己写一个 jwt 工具类(如果你有兴趣的话) 当然了,重复造轮子不是程序员的风格,我们主张拿来...

    JWT(一):认识 JSON WebToken
    JWT(二):使用 Java 实现 JWT

    介绍

    原理在上篇《JWT(一):认识 JSON Web Token》已经说过了,实现起来并不难,你可以自己写一个 jwt 工具类(如果你有兴趣的话)

    当然了,重复造轮子不是程序员的风格,我们主张拿来主义!
    鲁迅-又拿我说事儿.jpg

    JWT 官网提供了多种语言的 JWT 库,详情可以参考 https://jwt.io/#debugger 页面下半部分

    建议使用 jjwt库 ,它的github地址 https://github.com/jwtk/jjwt

    jjwt 版本 0.10.7,它和 0.9.x 有很大的区别,一定要注意!!!

    本文分5部分

    • 第1部分:以简单例子演示生成、验证、解析 jwt 过程
    • 第2部分:介绍 jjwt 的常用方法
    • 第3部分:封装一个常用的 jwt 工具类
      如果只是拿来主义,看到这里就可以了
    • 第4部分:介绍 jjwt 的各种签名算法
    • 第5部分:对 jwt 进行安全加密

    简单例子

    引入 MAVN 依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.10.7</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.10.7</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.10.7</version>
        <scope>runtime</scope>
    </dependency>
    

    一个例子

        
        // 生成密钥
        String key = "0123456789_0123456789_0123456789";
        SecretKey secretKey = new SecretKeySpec(key.getBytes(), SignatureAlgorithm.HS256.getJcaName());
    
        // 1. 生成 token
        String token = Jwts.builder()     // 创建 JWT 对象
                .setSubject("JSON Web Token")   // 设置主题(声明信息)
                .signWith(secretKey)    // 设置安全密钥(生成签名所需的密钥和算法)
                .compact(); // 生成token(1.编码 Header 和 Payload 2.生成签名 3.拼接字符串)
        System.out.println(token);
    
        //token = token + "s";
    
        // 2. 验证token,如果验证token失败则会抛出异常
        try {
            Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token);
            // OK, we can trust this token
            System.out.println("验证成功");
        } catch (JwtException e) {
            //don't trust the token!
            System.out.println("验证失败");
        }
    
        // 3. 解析token
        Claims body = Jwts.parser()     // 创建解析对象
                .setSigningKey(secretKey)   // 设置安全密钥(生成签名所需的密钥和算法)
                .parseClaimsJws(token)  // 解析token
                .getBody(); // 获取 payload 部分内容
        System.out.println(body);
    

    输出结果:

    eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKU09OIFdlYiBUb2tlbiJ9.QwmY_0qXW4BhAHcDpxz62v3xqkFYbg5lsZQhM2t-kVs
    验证成功
    {sub=JSON Web Token}
    

    常用方法

    以下内容建议参考源码获知更多详情

    Jwts.builder() 创建了 DefaultJwtBuilder 对象,该对象的常用方法如下:

    Header

    compact() 方法中会自动根据签名算法设置头部信息,当然也可以手动设置

    • setHeader(Header header): JwtBuilder
    • setHeader(Map<String, Object> header): JwtBuilder
    • setHeaderParams(Map<String, Object> params): JwtBuilder
    • setHeaderParam(String name, Object value): JwtBuilder

    参数 Header 对象 可通过 Jwts.header(); 创建,它简单得就像一个 map (把它当做 map 使用即可)

    Payload

    至少设置一个 claims,否则在生成签名时会抛出异常

    • setClaims(Claims claims): JwtBuilder
    • setClaims(Map<String, Object> claims): JwtBuilder
    • addClaims(Map<String, Object> claims): JwtBuilder
    • setIssuer(String iss): JwtBuilder
    • setSubject(String sub): JwtBuilder
    • setAudience(String aud): JwtBuilder
    • setExpiration(Date exp): JwtBuilder
    • setNotBefore(Date nbf): JwtBuilder
    • setIssuedAt(Date iat): JwtBuilder
    • setId(String jti): JwtBuilder
    • claim(String name, Object value: JwtBuilder

    参数对象 Claims 同 Header 类似,通过 Jwts.claims() 创建,同样简单得就像一个 map

    值得注意的一点是:不要在 setXxx 之后调用 setClaims(Claims claims) 或 setClaims(Map<String, Object> claims),因为这两个方法会覆盖所有已设置的 claim

    Signature

    • signWith(Key key)
    • signWith(Key key, SignatureAlgorithm alg)
    • signWith(SignatureAlgorithm alg, byte[] secretKeyBytes)
    • signWith(SignatureAlgorithm alg, String base64EncodedSecretKey)
    • signWith(SignatureAlgorithm alg, Key key)

    以上方法最终就是设置两个对象:key 和 algorithm,分别代表密钥和算法
    方法内部生成密钥使用的方法的和演示中的一样

    SecretKey key = new SecretKeySpec(secretKeyBytes, alg.getJcaName());
    

    注意:key 的长度必须符合签名算法的要求(避免生成弱密钥)
    HS256:bit 长度要>=256,即字节长度>=32
    HS384:bit 长度要>=384,即字节长度>=48
    HS512:bit 长度要>=512,即字节长度>=64
    在 secret key algorithms 名称中的数字代表了最小bit长度

    更多签名算法的详情,请参考签名算法小节

    封装 JWT 工具类

    package com.liuchuanv.jwt;
    
    import io.jsonwebtoken.*;
    import io.jsonwebtoken.security.SignatureException;
    
    import javax.crypto.spec.SecretKeySpec;
    import java.security.Key;
    import java.util.Date;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * JSON Web Token 工具类
     *
     * @author LiuChuanWei
     * @date 2019-12-11
     */
    public class JwtUtils {
    
        /**
         * key(按照签名算法的字节长度设置key)
         */
        private final static String SECRET_KEY = "0123456789_0123456789_0123456789";
        /**
         * 过期时间(毫秒单位)
         */
        private final static long TOKEN_EXPIRE_MILLIS = 1000 * 60 * 60;
    
        /**
         * 创建token
         * @param claimMap
         * @return
         */
        public static String createToken(Map<String, Object> claimMap) {
            long currentTimeMillis = System.currentTimeMillis();
            return Jwts.builder()
                    .setId(UUID.randomUUID().toString())
                    .setIssuedAt(new Date(currentTimeMillis))    // 设置签发时间
                    .setExpiration(new Date(currentTimeMillis + TOKEN_EXPIRE_MILLIS))   // 设置过期时间
                    .addClaims(claimMap)
                    .signWith(generateKey())
                    .compact();
        }
    
        /**
         * 验证token
         * @param token
         * @return 0 验证成功,1、2、3、4、5 验证失败
         */
        public static int verifyToken(String token) {
            try {
                Jwts.parser().setSigningKey(generateKey()).parseClaimsJws(token);
                return 0;
            } catch (ExpiredJwtException e) {
                e.printStackTrace();
                return 1;
            } catch (UnsupportedJwtException e) {
                e.printStackTrace();
                return 2;
            } catch (MalformedJwtException e) {
                e.printStackTrace();
                return 3;
            } catch (SignatureException e) {
                e.printStackTrace();
                return 4;
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                return 5;
            }
        }
    
        /**
         * 解析token
         * @param token
         * @return
         */
        public static Map<String, Object> parseToken(String token) {
            return Jwts.parser()  // 得到DefaultJwtParser
                    .setSigningKey(generateKey()) // 设置签名密钥
                    .parseClaimsJws(token)
                    .getBody();
        }
    
        /**
         * 生成安全密钥
         * @return
         */
        public static Key generateKey() {
           return new SecretKeySpec(SECRET_KEY.getBytes(), SignatureAlgorithm.HS256.getJcaName());
        }
    }
    
    

    测试代码如下:

      //Map<String, Object> map = new HashMap<String, Object>();
            //map.put("userId", 1002);
            //map.put("userName", "张晓明");
            //map.put("age", 12);
            //map.put("address", "山东省青岛市李沧区");
            //String token = JwtUtils.createToken(map);
            //System.out.println(token);
    
            String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0ZWM2NWNhNC0wZjVmLTRlOTktOTI5NS1mYWUyN2UwODIzYzQiLCJpYXQiOjE1NzY0OTI4NjYsImV4cCI6MTU3NjQ5NjQ2NiwiYWRkcmVzcyI6IuWxseS4nOecgemdkuWym-W4guadjuayp-WMuiIsInVzZXJOYW1lIjoi5byg5pmT5piOIiwidXNlcklkIjoxMDAyLCJhZ2UiOjEyfQ.6Z18aIA6y52ntQkV3BwlYiVK3hL3R2WFujjTmuvimww";
            int result = JwtUtils.verifyToken(token);
            System.out.println(result);
    
            Map<String, Object> map = JwtUtils.parseToken(token);
            System.out.println(map);
    

    输出结果:

    0
    {jti=4ec65ca4-0f5f-4e99-9295-fae27e0823c4, iat=1576492866, exp=1576496466, address=山东省青岛市李沧区, userName=张晓明, userId=1002, age=12}
    

    签名算法

    12 种签名算法

    JWT 规范定义了12种标准签名算法:3种 secret key 算法和9种非对称密钥算法

    • HS256: HMAC using SHA-256
    • HS384: HMAC using SHA-384
    • HS512: HMAC using SHA-512
    • ES256: ECDSA using P-256 and SHA-256
    • ES384: ECDSA using P-384 and SHA-384
    • ES512: ECDSA using P-521 and SHA-512
    • RS256: RSASSA-PKCS-v1_5 using SHA-256
    • RS384: RSASSA-PKCS-v1_5 using SHA-384
    • RS512: RSASSA-PKCS-v1_5 using SHA-512
    • PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
    • PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
    • PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512

    根据算法名称可分为四类:HSxxx(secret key 算法)、ESxxx、RSxxx、PSxxx

    HSxxx、ESxxx 中的 xxx 表示算法 key 最小 Bit 长度
    RSxxx、PSxxx 中的 xxx 表示算法 key 最小 Byte 长度

    规定key的最小长度是为了避免因 key 过短生成弱密钥

    生成密钥

    jjwt 生成 secret key 两种方法

    String key = "1234567890_1234567890_1234567890";
    // 1. 根据key生成密钥(会根据字节参数长度自动选择相应的 HMAC 算法)
    SecretKey secretKey1 = Keys.hmacShaKeyFor(key.getBytes());
    // 2. 根据随机数生成密钥
    SecretKey secretKey2 = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    
    • 方法 Keys.hmacShaKeyFor(byte[]) 内部也是 new SecretKeySpec(bytes, alg.getJcaName()) 来生成密钥的
    • 方法 Keys.secretKeyFor(SignatureAlgorithm) 内部使用 KeyGenerator.generateKey() 生成密钥

    jjwt 也提供了非对称密钥对的生成方法

    // 1. 使用jjwt提供的方法生成
    KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);    //or RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
    
    // 2. 手动生成
    int keySize = 1024;
    // RSA算法要求有一个可信任的随机数源
    SecureRandom secureRandom = new SecureRandom();
    // 为RSA算法创建一个KeyPairGenerator对象 
    KeyPairGenerator keyPairGenerator = null;
    try {
        keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    // 利用上面的随机数据源初始化这个KeyPairGenerator对象
    keyPairGenerator.initialize(keySize, secureRandom);
    // 生成密钥对
    KeyPair keyPair2 = keyPairGenerator.generateKeyPair();
    
    
    • Keys.keyPairFor(SignatureAlgorithm) 会根据算法自动生成相应长度的
    • signWith(secretKey) 会根据密钥长度自动选择相应算法,也可以指定任意算法(指定的算法不受密钥长度限制,可任意选择,即用 RS256生成的密钥,可以 signWith(secretKey, SignatureAlgorithm.RS512),但是 JJWT 并不建议这么做)
    • 在加密时使用 keyPair.getPrivate() ,解密时使用 keyPair.getPublic()

    不同密钥生成token

    以上都是使用同一密钥签名生成所有的token,下面我们使用不同的密钥

    这一个特性可以应用于不同用户/角色使用不同的密钥生成的 token,帮助你更好的构建权限系统

    1. 首先在 Header(或 claims)中设置一个 keyId

    2. 定义一个类,继承 SigningKeyResolverAdapter,并重写 resolveSigningKey() 或 resolveSigningKeyBytes() 方法

      public class MySigningKeyResolver extends SigningKeyResolverAdapter {
          @Override
          public Key resolveSigningKey(JwsHeader header, Claims claims) {
              // 除了从 header 中获取 keyId 外,也可以从 claims 中获取(前提是在 claims 中设置了 keyId 声明)
              String keyId = header.getKeyId();
              // 根据 keyId 查找相应的 key
              Key key = lookupVerificationKey(keyId);
              return key;
          }
      
          public Key lookupVerificationKey(String keyId) {
              // TODO 根据 keyId 获取 key,比如从数据库中获取
              // 下面语句仅做演示用,绝对不可用于实际开发中!!!
              String key = "qwertyuiopasdfghjklzxcvbnm2019_" + keyId;
              return Keys.hmacShaKeyFor(key.getBytes());
          }
      }
      
    3. 解析时,不再调用 setSigningKey(SecretKey) ,而是调用 setSigningKeyResolver(SigningKeyResolver)

      // 生成密钥
              // TODO 此处 keyId 仅做演示用,实际开发中可以使用 UserId、RoleId 等作为 keyId
              String keyId = new Long(System.currentTimeMillis()).toString();
              System.out.println("keyId=" + keyId);
      
              String key = "qwertyuiopasdfghjklzxcvbnm2019_" + keyId;
              SecretKey secretKey = new SecretKeySpec(key.getBytes(), SignatureAlgorithm.HS256.getJcaName());
      
              // 1. 生成 token
              String token = Jwts.builder()
                      .setHeaderParam(JwsHeader.KEY_ID, keyId)    // 设置 keyId(当然也可以在 claims 中设置)
                      .setSubject("JSON Web Token")
                      .signWith(secretKey)
                      .compact();
              System.out.println("token=" + token);
      
              // 2. 验证token
              // token 使用了不同的密钥生成签名,在解析时就不用调用 setSigningKey(SecretKey) 了
              // 而是调用 setSigningKeyResolver(SigningKeyResolver)
              try {
                  Jwts.parser()
                          .setSigningKeyResolver(new MySigningKeyResolver())
                          .parseClaimsJws(token);
                  // OK, we can trust this token
                  System.out.println("token验证成功");
              } catch (JwtException e) {
                  //don't trust the token!
                  System.out.println("token验证失败");
              }
      
      

    安全加密

    敬请期待 …

    展开全文
  • 需要使用到的两个jar包//jwtcom.nimbusdsnimbus-jose-jwt4.13.1//redisredis.clientsjedis2.1.0Jwt生成工具类/*** 生成Token工具类**/public class JwtToken {/*** 秘钥*/private static final byte[] SECRET="3d...

    需要使用到的两个jar包

    //jwt

    com.nimbusds

    nimbus-jose-jwt

    4.13.1

    //redis

    redis.clients

    jedis

    2.1.0

    Jwt生成工具类

    /**

    * 生成Token工具类

    *

    */

    public class JwtToken {

    /**

    * 秘钥

    */

    private static final byte[] SECRET="3d990d2276917dfac04467df11fff26d".getBytes();

    /**

    * 初始化head部分的数据为

    * {

    * "alg":"HS256",

    * "type":"JWT"

    * }

    */

    private static final JWSHeader header=new JWSHeader(JWSAlgorithm.HS256, JOSEObjectType.JWT, null, null, null, null, null, null, null, null, null, null, null);

    /**

    * 生成token,该方法只在用户登录成功后调用

    *

    * @param Map集合,可以存储用户id,token生成时间,token过期时间等自定义字段

    * @return token字符串,若失败则返回null

    */

    public static String createToken(Map payload) {

    String tokenString=null;

    // 创建一个 JWS object

    JWSObject jwsObject = new JWSObject(header, new Payload(new JSONObject(payload)));

    try {

    // 将jwsObject 进行HMAC签名

    jwsObject.sign(new MACSigner(SECRET));

    tokenString=jwsObject.serialize();

    } catch (JOSEException e) {

    System.err.println("签名失败:" + e.getMessage());

    e.printStackTrace();

    }

    return tokenString;

    }

    /**

    * 校验token是否合法,返回Map集合,集合中主要包含 state状态码 data鉴权成功后从token中提取的数据

    * 该方法在过滤器中调用,每次请求API时都校验

    * @param token

    * @return Map

    */

    public static Map validToken(String token) {

    Map resultMap = new HashMap();

    try {

    JWSObject jwsObject = JWSObject.parse(token);

    Payload payload = jwsObject.getPayload();

    JWSVerifier verifier = new MACVerifier(SECRET);

    if (jwsObject.verify(verifier)) {

    JSONObject jsonOBj = payload.toJSONObject();

    resultMap.put("state", TokenState.VALID.toString());

    if (jsonOBj.containsKey("ext")) {

    long extTime = Long.valueOf(jsonOBj.get("ext").toString());

    long curTime = new Date().getTime();

    if (curTime > extTime) {

    resultMap.clear();

    resultMap.put("state", TokenState.EXPIRED.toString());

    }

    }

    resultMap.put("data", jsonOBj);

    } else {

    // 校验失败

    resultMap.put("state", TokenState.INVALID.toString());

    }

    } catch (Exception e) {

    //e.printStackTrace();

    // token格式不合法导致的异常

    resultMap.clear();

    resultMap.put("state", TokenState.INVALID.toString());

    }

    return resultMap;

    }

    }

    //检验token状态类

    /**

    * 枚举,定义token的三种状态

    *

    */

    public enum TokenState {

    /**

    * 过期

    */

    EXPIRED("EXPIRED"),

    /**

    * 无效(token不合法)

    */

    INVALID("INVALID"),

    /**

    * 有效的

    */

    VALID("VALID");

    private String state;

    private TokenState(String state) {

    this.state = state;

    }

    /**

    * 根据状态字符串获取token状态枚举对象

    * @param tokenState

    * @return

    */

    public static TokenState getTokenState(String tokenState){

    TokenState[] states=TokenState.values();

    TokenState ts=null;

    for (TokenState state : states) {

    if(state.toString().equals(tokenState)){

    ts=state;

    break;

    }

    }

    return ts;

    }

    public String toString() {

    return this.state;

    }

    public String getState() {

    return state;

    }

    public void setState(String state) {

    this.state = state;

    }

    }

    展开全文
  • 介绍原理在上篇《JWT(一):认识 JSON Web Token》已经说过了,实现起来并不难,你可以自己写一个 jwt 工具类(如果你有兴趣的话)当然了,重复造轮子不是程序员的风格,我们主张拿来主义!JWT 官网提供了多种语言的 ...
  • package com.hthl.jwt;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import org.apache.commons.cod...
  • 介绍原理在上篇《JWT(一):认识 JSON Web Token》已经说过了,实现起来并不难,你可以自己写一个 jwt 工具类(如果你有兴趣的话)当然了,重复造轮子不是程序员的风格,我们主张拿来主义!鲁迅-又拿我说事儿.jpgJWT ...
  • Java实现JWT的Token认证机制

    千次阅读 2019-04-11 10:21:34
    基于JWT的Token认证机制实现 1.什么是JWT JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。 2.JWT组成 一个JWT实际上就是一个字符串,它由三部分组成,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 864
精华内容 345
关键字:

java实现jwt

java 订阅