【springboot】jwt实现token验证

什么是jwt

Json web token (JWT),用于进行身份验证,开销小,适用于单点登录。优点是token是以json加密的形式保存在客户端的,跨语言;能将用户需要的所有信息都加密到token里,不用多次查询数据库;不用在服务端保存会话信息,适用于分布式微服务。

  1. 用户使用账密发送post请求
  2. 服务器使用私钥创建jwt(生成签名)
  3. 服务器返回这个jwt给浏览器
  4. 浏览器将该jwt加在之后所有请求的请求头中
  5. 服务器拦截请求验证jwt(校验token)
  6. 验证通过则返回响应信息给浏览器

jwt构成:

  1. 头部(header):token类型和采用的加密算法
  2. 载荷(payload):存放有效信息:标准中注册的声明、公共的声明、私有的声明
  3. 签证(signature):签证信息:base64加密后的headerbase64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密
    https://jwt.io/#debugger-io

Debugger工具:https://jwt.io/#debugger-io

springboot集成jwt

依赖

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

自定义注解 @JwtToken

@Target({
    
    ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JwtToken {
    
    
    boolean required() default  true;
}

工具类,token的生成与校验

@Component
public class JwtUtil {
    
    
    /**
     * 静态方法调用非静态接口层(Service层)
     */
    public static JwtUtil jwtUtil; //声明对象

    @PostConstruct //初始化
    public void init() {
    
    
        jwtUtil = this;
        jwtUtil.userService = this.userService;
    }

    @Autowired //注入
            UserService userService;


    /**
     * 过期时间30分钟
     */
    private static final long EXPIRE_TIME = 30 * 60 * 1000;//自定义修改
    /**
     * jwt 密钥
     */
    private static final String SECRET = "abcde";//自定义修改

    /**
     * 生成签名,30分钟后过期
     *
     * @param userId
     * @return
     */
    public static String sign(String userId, String username) {
    
    
        try {
    
    
            //过期时间
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            //私钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            //附带username和userID生成签名,可自定义附带信息,这里是用户ID和用户名称
            return JWT.create()
                    // 将 user id 保存到 token 里面
                    .withClaim("userId", userId)
                    .withClaim("username", username)
                    // 分钟后token过期
                    .withExpiresAt(date)
                    // token 的密钥
                    .sign(algorithm);
        } catch (Exception e) {
    
    
            return null;
        }
    }

    /**
     * 校验token
     *
     * @param token
     * @return
     */
    public static boolean checkSign(String token) {
    
    
        try {
    
    
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            // 验证token
            DecodedJWT jwt = verifier.verify(token);
//            String subject = jwt.getSubject();
//            List<String> audience = jwt.getAudience();
			// 获取附带信息,并进行自定义验证,这里是验证根据userid查出来的用户名与token中附带的用户名是否一致
            Map<String, Claim> claims = jwt.getClaims();
            String userId = claims.get("userId").asString();
            String username = claims.get("username").asString();
            String un = jwtUtil.userService.getUsernameById(userId);
            if (!un.equals(username)) {
    
    
                throw new RuntimeException("token无效,请重新登录");
            }
            return true;
        } catch (JWTVerificationException exception) {
    
    
            throw new RuntimeException("无效token,请重新获取");
        }
    }
}

拦截器,拦截带有@JwtToken注解的请求,有的话进行验证

public class JwtInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    
    
        // 从 http 请求头中取出 token
        String token = httpServletRequest.getHeader("token");
        // 如果不是映射到方法直接通过
        if(!(o instanceof HandlerMethod)){
    
    
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)o;
        Method method=handlerMethod.getMethod();
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(JwtToken.class)) {
    
    
            JwtToken jwtToken = method.getAnnotation(JwtToken.class);
            if (jwtToken.required()) {
    
    
                // 执行认证
                if (token == null) {
    
    
                    throw new RuntimeException("无token,请重新登录");
                }
                // 验证 token
                JwtUtil.checkSign(token);
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    
    

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    
    

    }
}

controller,登录时不使用注解并将生成的token返回给客户端,其他需要进行身份验证的请求时使用@JwtToken

	@RequestMapping(value = "/login",method = RequestMethod.POST)
    public JsonResult login(@RequestParam(value = "username") String username, @RequestParam("password") String password){
    
    
        Map<String,Object> record=new HashMap<>();
        record.put("username",username);
        record.put("password",password);
        Map data=userService.login(record);
        if(data!=null){
    
    
            String token= JwtUtil.sign(data.get("userId").toString(),username);
            data.put("token",token);
            data.remove("userId");
            return JsonResult.build(1,"success",data);
        }else {
    
    
            return JsonResult.build(-1,"用户名或密码错误",null);
        }
    }
	@JwtToken
    @RequestMapping(value = "getList",method = RequestMethod.POST)
    public JsonResult getList(@RequestParam("a") String a){
    
    
        Map<String,Object> record=new HashMap<>();
        record.put("a",a);
        List list = userService.getList(record);
        return JsonResult.ok(list);
    }

前端开发请求

登录成功后得到token,可存放在sessionStorage、localStorage或cookie中

方案一:将token添加在header
例如使用axios

/* http request
 **请求拦截器
 **在发送请求之前进行的一系列处理,根据项目要求自行配置
 **例如:loading
 */
axios.interceptors.request.use(
  (config) => {
    
    
    // 请求响应时间
    config.timeout = 60 * 1000
    // config.data = JSON.stringify(config.data)
    config.headers = {
    
    
      'Content-Type': 'application/x-www-form-urlencoded',
      token: sessionStorage.getItem('accessToken')//获取token
      // 'Content-Type': 'application/json'
    }
    return config
  },
  function(error) {
    
    
    // 对请求错误做处理
    return Promise.reject(error)
  }
)

方案二:将token携带在get或post请求的参数中

猜你喜欢

转载自blog.csdn.net/lorogy/article/details/112367591