• JWT详解


    前言

    定义:JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案

    JWT官网

    由于HTTP协议是无状态的,这意味着如果我们想判定一个接口是否被认证后访问,就需要借助cookie或者session会话机制进行判定,但是由于现在的系统架构大部分都不止一台服务器,此时又要借助数据库或者全局缓存 做存储,这种方案显然受限太多。

    那么我们可不可以让认证令牌的发布者自己去识别这个令牌是不是我曾经发布的令牌呢(JWT核心思想),这是JWT最大的优点也是最大的缺点,优点是简单快捷、不需要依赖任何第三方操作就能实现身份认证,缺点就是对于任何拥有用户发布令牌的请求都会认证通过。

    JWT的数据结构

    正常的JWT数据结构应该如下

    它是一个很长的字符串,中间用点(.)分隔成三个部分

    JWT的三个部分依次: Header - 头部 、Payload - 负载 、Signature(签名)

    即:Header.Payload.Signature

    Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

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

    alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT

    Payload

    Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

    iss (issuer):签发人

    exp (expiration time):过期时间

    sub (subject):主题

    aud (audience):受众

    nbf (Not Before):生效时间

    iat (Issued At):签发时间

    jti (JWT ID):编号

    除了官方字段,你还可以在这个部分定义私有字段

    {
    "sub": "1234567890",
    "name": "John Doe",
    "age": "19"
    }

    注意:JWT默认是明文展示,任何人都可以读取到,所以此处不要放私密信息

    这个 JSON 对象也要使用 Base64URL 算法转成字符串。

    Signature

    Signature 部分是对前两部分的签名,防止数据篡改。

    首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

      HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)
    

    算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

    Base64URL

    前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

    JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法

    JWT的实现

    Maven依赖

     <dependency>
    		    <groupId>com.auth0</groupId>
    		    <artifactId>java-jwt</artifactId>
    		    <version>3.5.0</version>
    </dependency>
    

    JWT签名发布和验证代码

    public class TokenUtil {
    	//Token的过期时间
    	private static final long EXPIRE_TIME = 30 * 60 * 1000;
    	//Token的私钥
    	private static final String TOKEN_SECRET = "jytoken_secret";
    	
    	
    	/**
    	 * 生成签名,30分钟过期
    	 * @param **userInfo**	用户信息 用户姓名
    	* @param **other** 用户其他信息 用户id
    	* @return
    	 */
    	public static String sign(String userInfo, String other) {
    	    try {
    	    	// 设置过期时间
    	    	Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
    	    	//私钥和加密算法
    	        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
    	        // 设置头部信息
    	        Map<String, Object> header = new HashMap<>(2);
    	        header.put("Type", "Jwt");
    	        header.put("alg", "HS256");
    	        // 返回token字符串
    	        return JWT.create()
    	                .withHeader(header)
    	                .withClaim("userInfo", userInfo)
    	                .withClaim("other", other)
    	                .withExpiresAt(date)
    	                .sign(algorithm);
    	    } catch (Exception e) {
    	        e.printStackTrace();
    	        return null;
    	    }
    	}
    	
    	/**
    	 * 生成签名,30分钟过期
    	 * @param **userInfo**	用户信息 用户姓名
    	* @param **other** 用户其他信息 用户id
    	* @return
    	 */
    	public static String sign(String userInfo, String other,long expire) {
    	    try {
    	    	// 设置过期时间
    	    	Date date = new Date(System.currentTimeMillis() + expire);
    	    	//私钥和加密算法
    	        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
    	        // 设置头部信息
    	        Map<String, Object> header = new HashMap<>(2);
    	        header.put("Type", "Jwt");
    	        header.put("alg", "HS256");
    	        // 返回token字符串
    	        return JWT.create()
    	                .withHeader(header)
    	                .withClaim("userInfo", userInfo)
    	                .withClaim("other", other)
    	                .withExpiresAt(date)
    	                .sign(algorithm);
    	    } catch (Exception e) {
    	        e.printStackTrace();
    	        return null;
    	    }
    	}
    	
    	
    	/**
    	 * 检验token是否正确
    	 * @param **token**
    	* @return
    	 */
    	public static boolean verify(String token){
    	    try {
    	        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
    	        JWTVerifier verifier = JWT.require(algorithm).build();
    	        verifier.verify(token);//未验证通过会抛出异常
    	        return true;
    	    } catch (Exception e){
    	        return false;
    	    }
    	}
    	
    	/**
    	 * 从token中获取info信息
    	 * @param **token**
    	* @return
    	 */
    	public static String getUserName(String token,String info){
    	    try {
    	        DecodedJWT jwt = JWT.decode(token);
    	        return jwt.getClaim(info).asString();
    	    } catch (JWTDecodeException e){
    	        e.printStackTrace();
    	    }
    	    return null;
    	}
    	
    }
    
    

    拦截器配置无需认证的请求

    @Configuration
    public class InterceptorConfig extends WebMvcConfigurationSupport  {
       
    	@Autowired
    	private TokenHandler tokenHandler;
        
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            List<String> excludePath = new ArrayList<>();
            String checkLogin = "/pushlogin/checkIsCanLogin";
            String login = "/pushlogin/login";
            String getVerifyCode = "/common/send";
            String verfifyMethod = "/common/validationCode";
            excludePath.add(checkLogin);
            excludePath.add(login);
            excludePath.add(getVerifyCode);
            excludePath.add(verfifyMethod);
            registry.addInterceptor(tokenHandler).excludePathPatterns(excludePath);
        }
    }
    

    Token统一拦截器代码

    @Component
    @Slf4j
    public class TokenHandler implements HandlerInterceptor{
    	
    		@Override
    	   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  throws Exception {
    	 
    	        String token = request.getHeader("Authentication");
    	        if (token != null){
    	            boolean result = TokenUtil.verify(token);
    	            if(result){
    	                log.info("通过拦截器");
    	                return true;
    	            }
    	        }
    	        log.info("认证失败");
    	        
    	        return false;
    	   }
    	
    }
    

    用户登录时验证用户信息后,返回Token信息

     	@Override
        public UserDTO selectIsExistUserInfo(String phone) {
            //TODO 伪代码 验证用户信息 
            UserDTO info = 查询用户信息
            if (info != null) {
                String token = TokenUtil.sign(info.getUsername(), info.getUserId(), 6 * 60 * 60 * 1000);
                info.setToken(token);
            }
            return info;
        }
    
  • 相关阅读:
    each和foreach的区别
    apply和call的用法
    js小知识点
    关于react的一些疑问点
    c语言字符动画的实现
    解决'chromedriver' executable needs to be in PATH问题!
    二叉树的创建和遍历
    dns和dhcp
    编写一个application程序实现如下功能:接受命令行中给出的一个字符串,先将字符串原样输出,然后判断该穿的第一个字母是否为大写,若是大写则统计该串中大写字母的个数,并将所有大写字母输出。
    学生成绩管理系统究极版
  • 原文地址:https://www.cnblogs.com/dwlovelife/p/11321541.html
Copyright © 2020-2023  润新知