我的bug呢 2024-03-02 11:31 采纳率: 57.1%
浏览 9
已结题

SpringMvc中TokenAuthenticationFilter被访问两次

SpringSecurity中,自定义了TokenAuthenticationFilter继承OncePerRequestFilter之后,访问Controller后,该过滤器会被调用两次

@Slf4j
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private RedisCache redisCache;


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        System.out.println(httpServletRequest.getMethod());
        System.out.println(httpServletRequest);
        System.out.println("a");
        //登录接口,直接放行
//        if (httpServletRequest.getRequestURI().equals(SecurityConst.LOGIN_PATH)) {
//            filterChain.doFilter(httpServletRequest, httpServletResponse);
//            return;
//        }

//        获取请求头中的token信息
//        String token = httpServletRequest.getHeader(SecurityConst.TOKEN_ATTRIBUTE);
//        //不为空,解析token
//        String username = JwtUtil.getUserName(token);
//        //存储认证信息
//        UsernamePasswordAuthenticationToken authenticationToken =
//                new UsernamePasswordAuthenticationToken(username, null, null);
//        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }


}


密码校验过滤器


package com.cll.jtool.security.core;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cll.jtool.common.util.JwtUtil;
import com.cll.jtool.security.constant.SecurityConst;
import com.cll.jtool.security.domain.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import nonapi.io.github.classgraph.json.JSONUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;

/**
 * 自定义认证过滤器
 */

@Component
@Slf4j
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {


    public LoginAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }


    /**
     * 尝试认证,可以将用户名和密码从请求中解析出来
     *
     * @param request  from which to extract parameters and perform the authentication
     * @param response the response, which may be needed if the implementation has to do a
     *                 redirect as part of a multi-stage authentication process (such as OpenID).
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        User user = null;
//        try {
//            //避免修改原来的流对象,这里先序列化再反序列化
//            user = new ObjectMapper().readValue(request.getInputStream(), User.class);
//        } catch (Exception e) {
//            log.info("异常消息;{}",e.getMessage());
//            throw new RuntimeException("用户名密码获取错误");
//        }
        user = new User();
        user.setPassword("admin");
        user.setUsername("admin");
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        //拿到认证信息,去认证
        return this.getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);
    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        Authentication authentication = this.attemptAuthentication(servletRequest, servletResponse);
        if (authentication != null) {
            this.successfulAuthentication(servletRequest, servletResponse, chain, authentication);
        } else {
//            this.unsuccessfulAuthentication(servletRequest, servletResponse, null);
        }
        chain.doFilter(request,response);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws ServletException, IOException {
        super.successfulAuthentication(request, response, chain, authResult);
        String username = (String) authResult.getPrincipal();
        String token = JwtUtil.createJWT(username);
    }


    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        super.unsuccessfulAuthentication(request, response, failed);
    }
}

第一次调用访问道Controller后,该过滤器又会被调用一次,并且第二次的方法类型为GET,

img

  • 写回答

5条回答 默认 最新

  • Kwan的解忧杂货铺 Java领域优质创作者 2024-03-02 11:31
    关注

    我的bug呢 上午好☀️☀️☀️️
    本答案参考ChatGPT-3.5

    这种情况可能是由于你在Spring Security配置中同时使用了TokenAuthenticationFilter和LoginAuthenticationFilter两个过滤器,并且它们都使用了相同的URL模式来拦截请求。当请求到达控制器时,首先会被TokenAuthenticationFilter拦截,然后再被LoginAuthenticationFilter拦截,导致该过滤器被调用两次。

    解决这个问题的一种方法是修改URL模式,确保每个过滤器只拦截特定的请求。你可以根据自己的业务逻辑将URL模式进行调整,确保TokenAuthenticationFilter只拦截需要进行认证的请求,而LoginAuthenticationFilter只拦截登录请求。

    具体的修改方法如下:

    1. 在TokenAuthenticationFilter的doFilterInternal方法中,添加判断条件,只对需要进行认证的请求进行处理,其他请求直接放行。可以根据请求路径或者请求方法进行判断。
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        if (需要进行认证的条件) {
            // 处理认证逻辑
        } else {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }
    
    1. 在LoginAuthenticationFilter中,将URL模式修改为只拦截登录请求。
    @Component
    @Slf4j
    public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
        // ...
    
        // 修改登录请求的URL
        public LoginAuthenticationFilter(AuthenticationManager authenticationManager) {
            super(authenticationManager);
            this.setFilterProcessesUrl("/login");
        }
    
        // ...
    }
    

    通过以上修改,TokenAuthenticationFilter只会对需要进行认证的请求进行处理,LoginAuthenticationFilter只会拦截登录请求,不会再对已经认证的请求进行处理。这样就避免了TokenAuthenticationFilter被访问两次的问题。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

问题事件

  • 系统已结题 3月11日
  • 已采纳回答 3月3日
  • 创建了问题 3月2日

悬赏问题

  • ¥15 改进Yolov8时添加的注意力模块在task.py里检测不到
  • ¥50 高维数据处理方法求指导
  • ¥100 数字取证课程 关于FAT文件系统的操作
  • ¥15 如何使用js实现打印时每页设置统一的标题
  • ¥15 安装TIA PortalV15.1报错
  • ¥15 能把水桶搬到饮水机的机械设计
  • ¥15 Android Studio中如何把H5逻辑放在Assets 文件夹中以实现将h5代码打包为apk
  • ¥15 使用小程序wx.createWebAudioContext()开发节拍器
  • ¥15 关于#爬虫#的问题:请问HMDB代谢物爬虫的那个工具可以提供一下吗
  • ¥15 vue3+electron打包获取本地视频属性,文件夹里面有ffprobe.exe 文件还会报错这是什么原因呢?