为 Spring Boot 制作黑名单 JWT 令牌

Posted

技术标签:

【中文标题】为 Spring Boot 制作黑名单 JWT 令牌【英文标题】:Make Blacklist JWT tokens for spring boot 【发布时间】:2019-12-31 14:33:43 【问题描述】:

大家好,我已经在这个问题上待了几个星期 使用 Spring Boot 将 JWT 列入芯片黑名单 这是当用户尝试断开他在给定 mongoDB 数据库中存储他的密钥令牌并且它可以工作时我所做的

@PutMapping(value = "/destroy", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public JwtBlacklist logout(@RequestBody Map<String,String> json, HttpSession httpSession) throws UnsupportedEncodingException 

    String token = json.get("token");

    JwtBlacklist jwtBlacklist = new JwtBlacklist();
    jwtBlacklist.setToken(token);
    jwtBlacklistRepository.save(jwtBlacklist);

    return jwtBlacklistRepository.save(jwtBlacklist);
   

它将密钥完美地存储在给定的数据库中,无需担心 这是图片 https://ibb.co/dcX0Vnh"tokenStore"

现在最大的问题是,当我尝试将用户在连接期间必须使用的令牌列入黑名单并且断开连接时,此令牌在此处不再有效,代码 I JWTFilter.java

public class JWTFilter extends GenericFilterBean 
    @Value("$app.jwtSecret")
    public String jwtsecret;
    @Autowired
    public JwtBlacklistRepository jwtBlacklistRepository;

    @Override
    public void doFilter(final ServletRequest req,
                         final ServletResponse res,
                         final FilterChain chain) throws IOException, ServletException 

        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        final String authHeader = request.getHeader("authorization");

        if ("OPTIONS".equals(request.getMethod())) 
            response.setStatus(HttpServletResponse.SC_OK);

            chain.doFilter(req, res);
         else 

            if (authHeader == null || !authHeader.startsWith("Bearer ")) 
                throw new ServletException("Missing or invalid Authorization header");
            

            final String token = authHeader.substring(7);
            JwtBlacklist blacklist = this.jwtBlacklistRepository.findByTokenEquals(token);

                    if(blacklist == null) 
                        final Claims claims = Jwts.parser().setSigningKey("topsecretjwtpass".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();
                        request.setAttribute("claims", claims);
                     else 
                        throw new ServletException("Invalid token." + "");

                    

            chain.doFilter(req, res);
        

    
 

现在,当我尝试使用黑名单令牌或不使用黑名单令牌进行查询时,出现此错误

"timestamp":"2019-08-27T11:54:52.063+0000","status":500,"error":"内部 服务器错误","消息":"没有消息 可用","trace":"java.lang.NullPointerException\n\tat com.monarque.bank.monarque.config.JWTFilter.doFilter(JWTFilter.java:47)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:151)\n\tat org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:835)\n","path":"/v1/users/fa26eea5-7dce-4d79-a7f7-1f848cc58966"


这是模型

public class JwtBlacklist 
    @Id
    private String _id;
    @Indexed(direction = IndexDirection.ASCENDING)
    private String token;

    public String get_id() 
        return _id;
    

    public void set_id(String _id) 
        this._id = _id;
    

    public String getToken() 
        return token;
    

    public void setToken(String token) 
        this.token = token;
    

    @Override
    public String toString() 
        return "JwtBlacklist" +
                "_id='" + _id + '\'' +
                ", token='" + token + '\'' +
                '';
    


这是 JwtBlacklist 的存储库


import com.monarque.bank.monarque.dao.models.JwtBlacklist;
import org.springframework.data.mongodb.repository.MongoRepository;


public interface JwtBlacklistRepository extends MongoRepository<JwtBlacklist,String> 


JwtBlacklist findByTokenEquals(String token);


【问题讨论】:

您好,您应该将其作为构造函数接收,而不是在 JWTFilter 中使用 @Autowired,并将其传递到您正在创建 JWTFilter 实例的地方。可能在 SecurityConfig 或其他东西中。另一种选择是使 JWTFilter 成为一个组件,然后自动装配将起作用 你可以举个例子让我更好理解 嗨 @taylor-marshall 刚刚添加了一个答案 【参考方案1】:

总而言之,问题的发生是因为您的 JWTFilter 不是由 Spring 管理的,因此您不能只要求他为您注入 bean 和属性。

幸运的是,有一种简单的方法可以让事情顺利进行!

尝试看看添加以下 init 方法是否适合您:

public class JWTFilter extends GenericFilterBean 
  @Value("$app.jwtSecret")
  public String jwtsecret;
  @Autowired
  public JwtBlacklistRepository jwtBlacklistRepository;

  //ask spring to inject the values based on current context
  @PostConstruct
  public void init() 
    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
  

  @Override
  public void doFilter(final ServletRequest req,
                       final ServletResponse res,
                       final FilterChain chain) throws IOException, 
  ServletException 

  ....
  

这个方法,让spring帮你打针。

【讨论】:

我被困在弹簧靴的黑暗中好几个星期了,你来救我脱离这个地狱我的黑骑士谢谢你❤️ @TaylorMarshall 很高兴我能提供这么多帮助 =)【参考方案2】:

您收到此错误是因为您在 Filter Bean 中工作并且将 Bean 注入 GenericFilter 不起作用。 我知道解决此问题的一种方法是延迟加载您的存储库。

如果您的存储库为空,则只需检查 doFilter() 的第一行,如果是 获取您的 servletContext 并延迟加载它。 例如:

if (jwtBlacklistRepository == null)  //Lazy Load because filter
    ServletContext servletContext = req.getServletContext();
    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    jwtBlacklistRepository = webApplicationContext.getBean(JwtBlacklistRepository.class);

基本上,您是在强制将该 bean 加载到您的过滤器中。 (如果我对这个陈述有误,请纠正我)。

【讨论】:

谢谢你这么快回答我试试你的方法它适用于黑名单 jwt 令牌他的步行不是他的找不到被列入黑名单的令牌 请您编写代码以了解 JWTFilter.java 中哪个键被列入黑名单,这将是超级酷和你好 对不起,我很难理解,因为英语不是我的母语,我猜也不是你的。你能通过deepl.com/translator翻译吗?很难理解您的第一条评论。 在 JWT 黑名单存储库中找不到您的令牌是否正确?您的模型和存储库如何?以及为什么要强制使用 .substring(7) 令牌。 大声笑注意到你我的英语实际上很糟糕我说法语所以我尝试了你的方法并且它有效问题是如果你可以制作一个完整的代码人你不能知道列入黑名单的令牌它将是对你来说超级酷

以上是关于为 Spring Boot 制作黑名单 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Boot jwt 将身份验证系统连接到 React.js

Laravel JWT 令牌总是被列入黑名单

Spring Boot > 2.2.7 的令牌类型为“at+jwt”时 JWT 错误的解码

Spring boot JWT Auth 详细信息为空

Spring/Boot - JWT 未在 GET 请求中获取

Spring Boot with Spring Boot:将基本身份验证与JWT令牌身份验证相结合[复制]