简介
说明
本文用示例介绍如何解决拦截器中注入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 private 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 private 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 public 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 private 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; } }
请先
!