乐趣区

关于java:深入总结SpringBoot整合JWT这应该是全网讲的最通俗易懂的了

JWT

JWT(JSON Web Token) 是为了在网络应用环境间传递申明而执行的一种基于 JSON 的凋谢规范。

举例登录过程



在这里集体整顿了一些材料,有须要的敌人能够间接点击支付。

Java 基础知识大全

22 本 Java 架构师外围书籍

从 0 到 1Java 学习路线和材料

1000+ 道 2021 年最新面试题

组成

JWT 具体长什么样呢?
JWT 是由三段信息形成的,将这三段信息文本用. 链接一起就形成了 JWT 字符串。就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

元素

header

JWT 的头部承载两局部信息:

  • 申明类型,这里是 JWT;
  • 申明加密的算法,通常间接应用 HMAC SHA256;
  • 残缺的头部就像上面这样的 JSON:

    {
      'typ': 'JWT',
      'alg': 'HS256'
    }
    

    应用 base64 加密,形成了第一局部。

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    

    playload(重点)

    载荷就是寄存无效信息的中央,这些无效信息蕴含三个局部:

  • 规范中注册的申明;
  • 公共的申明;
  • 公有的申明;
    其中,规范中注册的申明 (倡议但不强制应用) 包含如下几个局部:
  • iss: jwt 签发者;
  • sub: jwt 所面向的用户;
  • aud: 接管 jwt 的一方;
  • exp: jwt 的过期工夫,这个过期工夫必须要大于签发工夫;
  • nbf: 定义在什么工夫之前,该 jwt 都是不可用的;
  • iat: jwt 的签发工夫;
  • jwt 的惟一身份标识,次要用来作为一次性 token, 从而回避重放攻打;
    公共的申明局部:公共的申明能够增加任何的信息,个别增加用户的相干信息或其余业务须要的必要信息,但不倡议增加敏感信息,因为该局部在客户端可解密。

公有的申明局部:公有申明是提供者和消费者所独特定义的申明,个别不倡议寄存敏感信息,因为 base64 是对称解密的,意味着该局部信息能够归类为明文信息。

定义一个 payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

而后将其进行 base64 加密,失去 Jwt 的第二局部:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

jwt 的第三局部是一个签证信息,这个签证信息由三局部组成:

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
 
 
var signature = HMACSHA256(encodedString, '密钥');
加密之后,失去 signature 签名信息。TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt 最终格局

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

secret 用来进行 jwt 的签发和 jwt 的验证,所以,在任何场景都不应该表露进来。

元素

SpringBoot 整合 JWT【正片】

引入依赖

<!--token-->
    <!-- jwt 反对 -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
    </dependency>

创立 JWT 工具类

留神动态属性的配置文件注入形式:

package com.neuq.common.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.neuq.common.exception.ApiException;
import com.neuq.common.response.ResultInfo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Author: xiang
 * @Date: 2021/5/11 21:11
 * <p>
 * JwtToken 生成的工具类
 * JWT token 的格局:header.payload.signature
 * header 的格局(算法、token 的类型), 默认:{"alg": "HS512","typ": "JWT"}
 * payload 的格局 设置:(用户信息、创立工夫、生成工夫)* signature 的生成算法:* HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 */

@Component
@ConfigurationProperties(prefix = "jwt")
public class JWTUtils {

    // 定义 token 返回头部
    public static String header;

    //token 前缀
    public static String tokenPrefix;

    // 签名密钥
    public static String secret;

    // 有效期
    public static long expireTime;

    // 存进客户端的 token 的 key 名
    public static final String USER_LOGIN_TOKEN = "USER_LOGIN_TOKEN";

    public void setHeader(String header) {JWTUtils.header = header;}

    public void setTokenPrefix(String tokenPrefix) {JWTUtils.tokenPrefix = tokenPrefix;}

    public void setSecret(String secret) {JWTUtils.secret = secret;}

    public void setExpireTime(int expireTimeInt) {JWTUtils.expireTime = expireTimeInt*1000L*60;}

    /**
     * 创立 TOKEN
     * @param sub
     * @return
     */
    public static String createToken(String sub){return tokenPrefix + JWT.create()
                .withSubject(sub)
                .withExpiresAt(new Date(System.currentTimeMillis() + expireTime))
                .sign(Algorithm.HMAC512(secret));
    }


    /**
     * 验证 token
     * @param token
     */
    public static String validateToken(String token){
        try {return JWT.require(Algorithm.HMAC512(secret))
                    .build()
                    .verify(token.replace(tokenPrefix, ""))
                    .getSubject();} catch (TokenExpiredException e){throw new ApiException(ResultInfo.unauthorized("token 曾经过期"));
        } catch (Exception e){throw new ApiException(ResultInfo.unauthorized("token 验证失败"));
        }
    }

    /**
     * 查看 token 是否须要更新
     * @param token
     * @return
     */
    public static boolean isNeedUpdate(String token){
        // 获取 token 过期工夫
        Date expiresAt = null;
        try {expiresAt = JWT.require(Algorithm.HMAC512(secret))
                    .build()
                    .verify(token.replace(tokenPrefix, ""))
                    .getExpiresAt();} catch (TokenExpiredException e){return true;} catch (Exception e){throw new ApiException(ResultInfo.unauthorized("token 验证失败"));
        }
        // 如果残余过期工夫少于过期时常的个别时 须要更新
        return (expiresAt.getTime()-System.currentTimeMillis()) < (expireTime>>1);
    }
}

yaml 属性配置

jwt:
  header: "Authorization" #token 返回头部
  tokenPrefix: "Bearer" #token 前缀
  secret: "qwertyuiop7418520" #密钥
  expireTime: 1 #token 无效工夫 (分钟) 倡议一小时以上 

登录办法将用户信息存入 token 中返回

  @Override
  public Map<String,Object> login(User user) {
      //phone 是除 id 外的惟一标记 须要进行查看
      if (user.getPhone() == null || user.getPhone().equals(""))
          throw new ApiException("手机号不非法");
      User selectUser = userDao.selectUserByPhone(user.getPhone());
      if (selectUser == null) {
          // 注册用户
          int count = userDao.insertUser(user);
          if (count < 1) throw new ApiException(ResultInfo.serviceUnavailable("注册异样"));
      }
      // 将 userId 存入 token 中
      String token = JWTUtils.createToken(selectUser.getUserId().toString());
      Map<String,Object> map = new HashMap<>();
      map.put("user",selectUser);
      map.put("token",token);
      return map;
  }

留神将 token 保留到 Http 的 header

    @GetMapping("/login")
    public ResultInfo login(User user, HttpServletResponse response) {Map<String, Object> map = userService.login(user);
        // 将 token 存入 Http 的 header 中
        response.setHeader(JWTUtils.USER_LOGIN_TOKEN, (String) map.get("token"));
        return ResultInfo.success((User)map.get("user"));
    }

拦截器验证每次申请的 token

/**
 * @Author: xiang
 * @Date: 2021/5/7 20:56
 * <p>
 * 拦截器:验证用户是否登录
 */
public class UserLoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //http 的 header 中取得 token
        String token = request.getHeader(JWTUtils.USER_LOGIN_TOKEN);
        //token 不存在
        if (token == null || token.equals("")) throw new ApiException(" 请先登录 ");
        // 验证 token
        String sub = JWTUtils.validateToken(token);
        if (sub == null || sub.equals(""))
            throw new ApiException(ResultInfo.unauthorized("token 验证失败"));
        // 更新 token 无效工夫 (如果须要更新其实就是产生一个新的 token)
        if (JWTUtils.isNeedUpdate(token)){String newToken = JWTUtils.createToken(sub);
            response.setHeader(JWTUtils.USER_LOGIN_TOKEN,newToken);
        }
        return true;
    }
}
@Configuration
@ComponentScan(basePackages = "com.neuq.common") // 全局异样解决类须要被扫描能力
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserLoginInterceptor())
                .addPathPatterns("/user/**")
                .addPathPatterns("/userInfo/**")
                .excludePathPatterns("/user/login");// 凋谢登录门路
    }

}

单点登录

将 token 或者一个惟一标识 UUID=UUID.randomUUID().toString() 存进 Cookie 中(别存在 Http 的 header 中了),设置门路为整个我的项目根门路 /*;
往往以这个惟一标识为 key,用户信息为 value 缓存在服务器中!!!

最初

感激大佬能看到最初,感觉文章对你有帮忙记得点个赞!

退出移动版