Spring拦截器中注入Bean失败解放方案详解

目录
  • 简介
  • 问题重现
  • 解决方案

简介

说明

本文用示例介绍如何解决拦截器中注入Bean失败的问题。

场景

Token拦截器中需要用@Autowired注入JavaJwtUtil类,结果发现注入的JavaJwtUtil为Null。

原因

拦截器的配置类是以new JwtInterceptor的方式使用的,那么这个JwtInterceptor不受Spring管理。因此,里边@Autowired注入JavaJwtUtil是不会注入进去的。

问题重现

代码

application.yml

server:
  port: 8080
spring:
  application:
    name: springboot-jwt
config:
  jwt:
    # 密钥
    secret: abcd1234
    # token过期时间(5分钟)。单位:毫秒.
    expire: 300000

拦截器配置

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JwtInterceptor());
    }
}

拦截器

package com.example.demo.interceptor;
import com.example.demo.util.JavaJwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Component
public class JwtInterceptor implements HandlerInterceptor {
    @Autowired
    JavaJwtUtil javaJwtUtil;
    List<String> whiteList = Arrays.asList(
            "/auth/login",
            "/error"
    );
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //放过不需要验证的页面。
        String uri = request.getRequestURI();
        if (whiteList.contains(uri)) {
            return true;
        }
        // 头部和参数都查看一下是否有token
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
            if (StringUtils.isEmpty(token)) {
                throw new RuntimeException("token是空的");
            }
        }
        if (!javaJwtUtil.verifyToken(token)) {
            log.error("token无效");
            return false;
        }
        String userId = javaJwtUtil.getUserIdByToken(token);
        log.info("userId:" + userId);
        String userName = javaJwtUtil.getUserNameByToken(token);
        log.info("userName:" + userName);
        return true;
    }
}

Jwt工具类

package com.example.demo.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JavaJwtUtil {
    //过期时间
    @Value("${config.jwt.expire}")
    private Long EXPIRE_TIME;
    //密钥
    @Value("${config.jwt.secret}")
    private String SECRET;
    // 生成Token,五分钟后过期
    public String createToken(String userId) {
        try {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    // 将 user id 保存到 token 里面
                    .withAudience(userId)
                    // date之后,token过期
                    .withExpiresAt(date)
                    // token 的密钥
                    .sign(algorithm);
        } catch (Exception e) {
            return null;
        }
    }
    // 根据token获取userId
    public String getUserIdByToken(String token) {
        try {
            String userId = JWT.decode(token).getAudience().get(0);
            return userId;
        } catch (JWTDecodeException e) {
            return null;
        }
    }
    // 根据token获取userName
    public String getUserNameByToken(String token) {
        try {
            String userName = JWT.decode(token).getSubject();
            return userName;
        } catch (JWTDecodeException e) {
            return null;
        }
    }
    //校验token
    public boolean verifyToken(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    // .withIssuer("auth0")
                    // .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (JWTVerificationException exception) {
//            throw new RuntimeException("token 无效,请重新获取");
            return false;
        }
    }
}

Controller

package com.example.demo.controller;
import com.example.demo.util.JavaJwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/auth")
public class AuthController {
    @Autowired
    JavaJwtUtil javaJwtUtil;
    @RequestMapping("/login")
    public String login() {
        // 验证userName,password和数据库中是否一致,如不一致,直接返回失败
        // 通过userName,password从数据库中获取userId
        String userId = 5 + "";
        String token = javaJwtUtil.createToken(userId);
        System.out.println("token:" + token);
        return token;
    }
    //需要token验证
    @RequestMapping("/info")
    public String info() {
        return  "验证通过";
    }
}

测试

访问:http://localhost:8080/auth/login

前端结果:一串token字符串

访问:http://localhost:8080/auth/info(以token作为header或者参数)

后端结果

java.lang.NullPointerException: null
	at com.example.demo.interceptor.JwtInterceptor.preHandle(JwtInterceptor.java:55) ~[main/:na]

解决方案

方案简述

配置类中将new JwtInterceptor()改为Bean的方式

配置类

package com.example.demo.interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getJwtInterceptor());
    }
    @Bean
    JwtInterceptor getJwtInterceptor() {
        return new JwtInterceptor();
    }
}

拦截器(此时无需@Component)

package com.example.demo.interceptor;
import com.example.demo.util.JavaJwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {
    @Autowired
    JavaJwtUtil javaJwtUtil;
    List<String> whiteList = Arrays.asList(
            "/auth/login",
            "/error"
    );
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //放过不需要验证的页面。
        String uri = request.getRequestURI();
        if (whiteList.contains(uri)) {
            return true;
        }
        // 头部和参数都查看一下是否有token
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
            if (StringUtils.isEmpty(token)) {
                throw new RuntimeException("token是空的");
            }
        }
        if (!javaJwtUtil.verifyToken(token)) {
            log.error("token无效");
            return false;
        }
        String userId = javaJwtUtil.getUserIdByToken(token);
        log.info("userId:" + userId);
        String userName = javaJwtUtil.getUserNameByToken(token);
        log.info("userName:" + userName);
        return true;
    }
}

到此这篇关于Spring拦截器中注入Bean失败解放方案详解的文章就介绍到这了,更多相关Spring Bean内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring为IOC容器注入Bean的五种方式详解

    这篇文章主要介绍了Spring为IOC容器注入Bean的五种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一 @Import导入组件,id默认是组件的全类名 //类中组件统一设置.满足当前条件,这个类中配置的所有bean注册才能生效: @Conditional({WindowsCondition.class}) @Configuration @Import({Color.class,Red.class,MyImportSelector

  • Spring为singleton bean注入prototype bean

    目录 环境 准备 测试0 测试1 测试2 测试3 注:不想看具体代码的话,可以直接看每个测试的总结. 环境 Ubuntu 22.04 IntelliJ IDEA 2022.1.3 JDK 17.0.3 Spring 5.3.21 准备 创建Maven项目 test0707 . 修改 pom.xml 文件,添加依赖: ...... <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <grou

  • Spring 多线程下注入bean问题详解

    本文介绍了Spring 多线程下注入bean问题详解,分享给大家,具体如下: 问题 Spring中多线程注入userThreadService注不进去,显示userThreadService为null异常 代码如下: public class UserThreadTask implements Runnable { @Autowired private UserThreadService userThreadService; @Override public void run() { AdeUs

  • 详解SpringBoot 多线程处理任务 无法@Autowired注入bean问题解决

    在多线程处理问题时,无法通过@Autowired注入bean,报空指针异常, 在线程中为了线程安全,是防注入的,如果要用到这个类,只能从bean工厂里拿个实例. 解决方法如下: 1.创建一个工具类代码: package com.hqgd.pms.common; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.spri

  • Spring注入Bean的一些方式总结

    通过注解注入Bean 背景 我们谈到Spring的时候一定会提到IOC容器.DI依赖注入,Spring通过将一个个类标注为Bean的方法注入到IOC容器中,达到了控制反转的效果.那么我们刚开始接触Bean的时候,一定是使用xml文件,一个一个的注入,就例如下面这样. <bean id="bean" class="beandemo.Bean" /> 我们的项目一般很大的话,就需要成千上百个Bean去使用,这样写起来就很繁琐.那么Spring就帮我们实现了一

  • SpringBoot通过注解注入Bean的几种方式解析

    目录 1.背景 xml扫描包的方式 2.通过注解注入的一般形式 2.1.Bean类 2.2.Configuration类 2.3.Test类 3.通过构造方法注入Bean 3.1.Bean类 3.2.AnotherBean类 3.3.Configuration类 4.通过set方法注入Bean 4.1.MyBean类 4.2.Configuration类和Test类 5.通过属性去注入Bean 6.通过List注入Bean 6.1.MyBeanList类 6.2.MyConfiguration类

  • 解决SpringBoot项目使用多线程处理任务时无法通过@Autowired注入bean问题

    最近在做一个"温湿度控制"的项目,项目要求通过用户设定的温湿度数值和实时采集到的数值进行比对分析,因为数据的对比与分析是一个通过前端页面控制的定时任务,经理要求在用户开启定时任务时,单独开启一个线程进行数据的对比分析,并将采集到的温湿度数值存入数据库中的历史数据表,按照我们正常的逻辑应该是用户在请求开启定时任务时,前端页面通过调用后端接口,创建一个新的线程来执行定时任务,然后在线程类中使用 @Autowired 注解注入保存历史数据的service层,在线程类中调用service层保存

  • Spring中如何动态注入Bean实例教程

    前言 在Spring中提供了非常多的方式注入实例,但是由于在初始化顺序的不同,基于标注的注入方式,容易出现未被正确注入成功的情况. 本文将介绍一种在实际项目中基于动态的方式来提取Spring管理的Bean. 下面话不多说了,来一起看看详细的介绍吧. 一.基于标注的方式注入实例 需要在Bean初始化之时,其依赖的对象必须初始化完毕.如果被注入的对象初始化晚于当前对象,则注入的对象将为null. 1.1 @Autowired 按照类型来加载Spring管理的Bean.默认情况下要求其Bean必须存在

  • Spring拦截器中注入Bean失败解放方案详解

    目录 简介 问题重现 解决方案 简介 说明 本文用示例介绍如何解决拦截器中注入Bean失败的问题. 场景 Token拦截器中需要用@Autowired注入JavaJwtUtil类,结果发现注入的JavaJwtUtil为Null. 原因 拦截器的配置类是以new JwtInterceptor的方式使用的,那么这个JwtInterceptor不受Spring管理.因此,里边@Autowired注入JavaJwtUtil是不会注入进去的. 问题重现 代码 application.yml server:

  • SpringMVC拦截器实现监听session是否过期详解

    本文主要向大家介绍了SpringMVC拦截器实现:当用户访问网站资源时,监听session是否过期的代码,具体如下: 一.拦截器配置 <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/user/login"/> <!-- 不拦截登录请求 --> <mvc:exclude-

  • Springmvc拦截器执行顺序及各方法作用详解

    实现HandlerInterceptor接口或者继承HandlerInterceptor的子类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ,下面讲实现其接口的写法,先看一下这个接口的三个方法. - 方法preHandle: 顾名思义,该方法将在请求处理之前进行调用,在controller之前执行.SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在

  • Spring之@Aspect中通知的5种方式详解

    目录 @Before:前置通知 案例 对应的通知类 通知中获取被调方法信息 JoinPoint:连接点信息 ProceedingJoinPoint:环绕通知连接点信息 Signature:连接点签名信息 @Around:环绕通知 介绍 特点 案例 对应的通知类 @After:后置通知 介绍 特点 对应的通知类 @AfterReturning:返回通知 用法 特点 案例 对应的通知类 @AfterThrowing:异常通知 用法 特点 案例 对应的通知类 几种通知对比 @Aspect中有5种通知

  • 在C#程序中注入恶意DLL的方法详解

    目录 一.背景 二.实现原理 1. 基本思路 2. 案例演示 3. 自定义注入 三:总结 一.背景 前段时间在训练营上课的时候就有朋友提到一个问题,为什么 Windbg 附加到 C# 程序后,程序就处于中断状态了?它到底是如何实现的?其实简而言之就是线程的远程注入,这一篇就展开说一下. 二.实现原理 1. 基本思路 WinDbg 在附加进程的时候,会注入一个线程到 C# 进程 中,注入成功后,会执行一个 DbgBreakPoint() 函数,其实就是 int 3 ,这时候 CPU 就会执行 3

  • Vue装饰器中的vue-property-decorator 和 vux-class使用详解

    目录 1. 安装 2. vue-property-decorator 3. vuex-class 目前在用vue开发的项目中,都会配合使用TypeScript进行一些约束.为了提高开发效率,往往会使用装饰器来简化我们的代码. 本文主要介绍装饰器vue-property-decorator 和 vux-class的使用. 1. 安装 npm i -S vue-property-decorator npm i -S vuex-class 2. vue-property-decorator @Comp

  • 在zuulFilter中注入bean失败的解决方案

    zuulFilter注入bean失败 一.为什么要用到这个 上周想实现在网关层 zuul 实现用户认证操作,即需要在网关过滤器中调用其他的微服务,按常规做法在 filter 中用 @Autowired 注解一个feign 接口,启动 一直失败,用度娘谷歌查了又查,只找到一些类似[在过滤器中注入bean]失败,但说的都是springMVC 并不是springcloud中的网关层 二.解决方法 查了很久,最终发现问题所在,其实在启动报错就提示很明显了,找不到相关实例,没错feign接口的实现类事实上

  • 解决Spring在Thread中注入Bean无效的问题

    目录 在Thread中注入Bean无效 错误的注入方法 通过封装Thread子类注入 通过外部引入 Spring多线程中bean的注入问题 网上的主要解决方法有 在Thread中注入Bean无效 在Spring项目中,有时需要新开线程完成一些复杂任务,而线程中可能需要注入一些服务.而通过Spring注入来管理和使用服务是较为合理的方式.但是若直接在Thread子类中通过注解方式注入Bean是无效的. 因为Spring本身默认Bean为单例模式构建,同时是非线程安全的,因此禁止了在Thread子类

  • Struts2拦截器Interceptor的原理与配置实例详解

    一.Struts2拦截器原理: Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的    拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器. 比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源:否则,系统直接转入登陆页面.对于上面的需求,可以在每个Action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用.因为大部分Action里的权限检查代码都大同小异,故

  • Spring Data MongoDB中实现自定义级联的方法详解

    前言 Spring Data MongoDB 项目提供与MongoDB文档数据库的集成,Spring与Hibernate集成时,Spring提供了org.springframework.orm.hibernate3.HibernateTemplate实现了对数据的CRUD操作, Spring Data MongoDB提供了org.springframework.data.mongodb.core.MongoTemplate对MongoDB的CRUD的操作,包括对集成的对象映射文件和POJO之间的

随机推荐