SpringBoot集成 Jwt 实现Token验证访问拦截

1、Jwt 简介

    JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作        为JSON对象传输。由于此信息是经过数字签名的,因此可以进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或        ECDSA的公钥/私钥对对JWT进行签名。 


2、应用场景

  • 授权:这是使用JWT最常见的方案。当用户登录后,每个后续请求将会在header带上JWT,允许用户访问允许使用该令牌的路由、服务和资源。单点登录是当今广泛使用JWT的一个特性,因为它具有较小的开销和易于跨不同域使用的能力。
  • 信息交换:JWT是保证各方之间安全地传输信息的一种好方法。因为JWT可以被签名,例如使用公钥/私钥对,可以确保发件人是他们所说的人。另外,由于使用header和playload计算签名,还可以验证内容是否被篡改。 

 3、Jwt结构

     JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔,分别是:

  • 标头
  • 有效载荷
  • 签名

     因此,JWT通常如下所示。

   xxxxx.yyyyy.zzzzz


4、Jwt工作流程

SpringBoot集成 Jwt 实现Token验证访问拦截_第1张图片

     1. 用户使用账号和面发出post请求;
     2. 服务器使用私钥创建一个jwt;
     3. 服务器返回这个jwt给浏览器;
     4. 浏览器将该jwt串在请求头中像服务器发送请求;
     5. 服务器验证该jwt;
     6. 返回响应的资源给浏览器。


5、SpringBoot 集成 Jwt

  • 工作目录介绍

没有连接数据库

SpringBoot集成 Jwt 实现Token验证访问拦截_第2张图片

  • pom配置文件



  4.0.0
  
    org.springframework.boot
    spring-boot-starter-parent
    2.3.2.RELEASE
     
  
  com.example
  loginintercept
  0.0.1-SNAPSHOT
  loginintercept
  Demo project for Spring Boot

  
    1.8
  

  
    
      org.springframework.boot
      spring-boot-starter-web
    

    
    
      io.jsonwebtoken
      jjwt
      0.7.0
    

    
    
      org.projectlombok
      lombok
      1.18.12
    

    
      org.springframework.boot
      spring-boot-starter-test
      test
      
        
          org.junit.vintage
          junit-vintage-engine
        
      
    
  

  
    
      
        org.springframework.boot
        spring-boot-maven-plugin
      
    
  


  • JwtUtils 工具类
package com.example.loginintercept.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import org.springframework.stereotype.Component;

/**
 * token 工具类
 *
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/7/30 2:23 下午 */ @Component public class JwtUtils { // 过期时间 private static long expire = 604800; // 秘钥 private static String secret = "HSyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; /** * 创建一个token * * @param userId * @return */ public String generateToken(String userId) { Date now = new Date(); Date expireDate = new Date(now.getTime() + expire); return Jwts.builder().setHeaderParam("type", "JWT").setSubject(userId).setIssuedAt(now) .setExpiration(expireDate).signWith( SignatureAlgorithm.HS512, secret).compact(); } /** * 解析token */ public Claims getClaimsByToken(String token) { try { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } catch (Exception e) { System.out.println("validate is token error"); return null; } } /** * 判断 token 是否过期 */ public boolean isTokenExpired(Date expiration){ return expiration.before(new Date()); } }

  • ResultT 统一返回格式
package com.example.loginintercept.config;

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

/**
 * TODO
 *
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/8/6 5:04 下午 */ public class ResultT extends HashMap { public ResultT() { put("code", 0); put("msg", "success"); } public static ResultT ok() { ResultT t = new ResultT(); t.put("msg", "操作成功"); return t; } public static ResultT ok(String msg) { ResultT t = new ResultT(); t.put("msg", msg); return t; } public static ResultT ok(Map map) { ResultT t = new ResultT(); t.putAll(map); return t; } public static ResultT error(int code, String msg) { ResultT t = new ResultT(); t.put("code", code); t.put("msg", msg); return t; } public static ResultT error() { return error(500, "未知异常"); } public ResultT put(String key, Object value){ super.put(key, value); return this; } }

  • TokenInterceptor 创建一个token拦截器
package com.example.loginintercept.config;

import com.example.loginintercept.exception.TokenRuntimeException;
import io.jsonwebtoken.Claims;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * 创建一个 token 拦截器.
 * 需要继承 HandlerInterceptorAdapter,并且声明为spring的组件
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/7/30 2:19 下午 */ @Component public class TokenInterceptor extends HandlerInterceptorAdapter { // 注入jwt工具类 @Autowired private JwtUtils jwtUtils; // 重写 前置拦截方法 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1、从请求头中获取token String token = request.getHeader("token"); // 2、判断 token 是否存在 if (token == null ||"".equals(token)) { System.out.println("未登录"); // 这里可以自定义 抛出 token 异常 throw new TokenRuntimeException("未登录"); } // 3、解析token Claims claim = jwtUtils.getClaimsByToken(token); if (null == claim) { System.out.println("token 解析错误"); // 这里可以自定义 抛出 token 异常 throw new TokenRuntimeException("token 解析错误"); } // 4、判断 token 是否过期 Date expiration = claim.getExpiration(); boolean tokenExpired = jwtUtils.isTokenExpired(expiration); if (tokenExpired) { System.out.println("token已过期,请重新登录"); // 这里可以自定义 抛出 token 异常 throw new TokenRuntimeException("token已过期,请重新登录"); } // 5、 从 token 中获取员工信息 String subject = claim.getSubject(); // 6、去数据库中匹配 id 是否存在 (这里直接写死了) if (null == subject) { System.out.println("员工不存在"); // 这里可以自定义 抛出 token 异常 throw new TokenRuntimeException("员工不存在"); } // 7、成功后 设置想设置的属性,比如员工姓名 request.setAttribute("userId", subject); request.setAttribute("userName", "张三"); return true; } }

  • WebMvcConfig 设置拦截器
package com.example.loginintercept.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 设置拦截器.
 * 打上configuration 注解,标注为配置项
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/7/30 2:17 下午 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { // 注入 token 拦截器 @Autowired private TokenInterceptor interceptor; /** * 重写添加拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 添加自定义拦截器,并拦截对应 url registry.addInterceptor(interceptor).addPathPatterns("/gateway/**"); } }

  • GatewayController 模仿需要登录后才能访问的资源
package com.example.loginintercept.controller;

import com.example.loginintercept.config.ResultT;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * controller
 *
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/8/6 4:52 下午 */ @RestController @RequestMapping("/gateway") public class GatewayController { @RequestMapping(value = "/find",method = RequestMethod.POST) public ResultT find(){ return ResultT.ok("find one success"); } }

  • LoginController 登录
package com.example.loginintercept.controller;

import com.example.loginintercept.config.JwtUtils;
import com.example.loginintercept.config.ResultT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * 登录
 *
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/8/6 5:12 下午 */ @RestController public class LoginController { // 注入jwt工具类 @Autowired private JwtUtils jwtUtils; @RequestMapping(value = "/login", method = RequestMethod.POST) public ResultT login(String name, String psw) { String userId = "132"; String token = jwtUtils.generateToken(userId); return ResultT.ok().put("token", token); } }

  • SysRuntimeExceptionHandler 全局异常处理
package com.example.loginintercept.exception;

import com.example.loginintercept.config.ResultT;
import com.example.loginintercept.exception.TokenRuntimeException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理
 *
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/8/6 5:03 下午 */ @RestControllerAdvice public class SysRuntimeExceptionHandler { @ExceptionHandler(TokenRuntimeException.class) public ResultT tokenRuntimeException(TokenRuntimeException e) { e.printStackTrace(); return ResultT.error(e.getCode(), e.getMsg()); } @ExceptionHandler(Exception.class) public ResultT handlerException(Exception e){ e.printStackTrace(); return ResultT.error(); } }

  • TokenRuntimeException 自定义 token 异常
package com.example.loginintercept.exception;

import lombok.Data;

/**
 * 自定义 token 异常
 *
 * @author ch
 * @version 1.0.0
 * @since 1.0.0
 * 

* Created at 2020/8/6 4:58 下午 */ @Data public class TokenRuntimeException extends RuntimeException{ private Integer code = 401; private String msg; public TokenRuntimeException(String msg) { this.msg = msg; } }


6、运行测试

  • 首先访问需要 token 的资源

SpringBoot集成 Jwt 实现Token验证访问拦截_第3张图片

  • 登录 获取 token

SpringBoot集成 Jwt 实现Token验证访问拦截_第4张图片

  • 请求头中存放 token 再次访问需要 token 的资源

SpringBoot集成 Jwt 实现Token验证访问拦截_第5张图片

代码地址: [email protected]:ChenHaoXFN/loginintercept.git

                  https://github.com/ChenHaoXFN/loginintercept

参考文献:https://jwt.io/introduction/

                  https://www.jianshu.com/p/e88d3f8151db

 

你可能感兴趣的:(springBoot,其他,java,java)