SpringSecurity6配置requestMatchers().permitAll() 无效问题

版本

<spring-boot.version>3.0.2</spring-boot.version>
<jjwt.version>0.12.5</jjwt.version>

问题描述

题主在写 SpringSecurity6 + JWT 做登录认证开发。一路跟着教程叭叭的敲。等到接口验证的时候,发现我的登录接口虽然在SecurityConfig中配置了免验证,但是访问登录接口的时候还是被拦截了。

这里先直接点题说明题主出错的原因:
因为我配置的URL是全路径的,也就是携带着server.servlet.context-path的配置内容的,类似这样/bees/API/login/wechat,其中/bees就是我配置的context-path

我出问题的时候已经在网上找了很多解决方式。但是都没有解决我的问题。
当我在访问我的login接口的时候,系统依然进入了我的自定义验证过滤器中。
后面在通义千问中,AI给出的建议,配置Security的日志输出级别到DEBUG这样能有效地排查问题。

logging.level.org.springframework.security=DEBUG

这样将SpringSecurity的log级别调到DEBUG,在启动服务的时候发现了两行有用的日志
在这里插入图片描述
这是在遇到问题,根据各种文章修改完依然没能解决问题之后的唯一一点曙光。

解决:去掉/bees之后,再次请求登录接口,就不会再过我们自定义的认证拦截器了。

这就是题主的遇到的问题症结所在。

总结

三点。

  1. 检查你自定义的认证拦截器是不是交给Spring管理了(如果是可能会有问题)。
  2. 检查你的SecurityConfig配置类中是否有配置WebSecurityCustomizer这个Bean(没有需要加上)。
  3. 最后检查你需要免验证的URL是否跟我一样加了context-path(或者使用了通配符,题主试过通配符但是不太行,可能是我使用方式有问题)。

最后贴一个比较完整的代码片段

包括 SecurityConfig JwtAuthenticationFilter JwtTokenUtil

代码片段如下:

SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserServiceImpl userService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //关闭csrf和frameOptions,如果不关闭会影响前端请求接口
        http.csrf(AbstractHttpConfigurer::disable).headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));
        //开启跨域以便前端调用接口
        http.cors();
        //这里是配置的关键,决定哪些接口开启防护,哪些接口绕过防护
        // 配置访问控制规则
        http.authorizeHttpRequests(request ->
            // 指定特定接口无需验证即可访问,如微信登录
            request.requestMatchers(HttpMethod.POST, "/API/login/wechat").permitAll()
            // 其他所有以 "/bees/" 开头的接口需要认证才能访问
            .requestMatchers("/bees/**").authenticated()
        );
       //禁用session
       http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //将我们自定义的认证过滤器替换掉默认的认证过滤器,指定将自定义的Filter添加到某个指定的Filter之前或者之后
        http.addFilterBefore(new WeChatLoginFilter(jwtTokenUtil, userService), UsernamePasswordAuthenticationFilter.class);
        //指定认证错误处理器
        http.exceptionHandling().authenticationEntryPoint(new BeesAuthenticationFailEntryPoint())
                .accessDeniedHandler(new BeesDeniedHandler());
        return http.build();
    }

    /**
     * 指定加密器
     *
     * @return BCryptPasswordEncoder
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        // 提供自定义loadUserByUsername
        authProvider.setUserDetailsService(userService);
        // 指定密码编辑器
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public WebSecurityCustomizer ignoringCustomizer() {
        return (web) -> web.ignoring().requestMatchers(HttpMethod.POST, "/API/login/wechat");
    }
}

JwtAuthenticationFilter

/**
* 自定义的认证过滤器不交给Spring管理
*/
public class WeChatLoginFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(WeChatLoginFilter.class);

    private final JwtTokenUtil jwtTokenUtil;
    private final UserServiceImpl userService;

    public WeChatLoginFilter(JwtTokenUtil jwtTokenUtil, UserServiceImpl userService) {
        this.jwtTokenUtil = jwtTokenUtil;
        this.userService = userService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        log.info("---WeChatLoginFilter---");
        String authorizationS = request.getHeader("Authorization");
        if (!StringUtils.hasLength(authorizationS)) {
            chain.doFilter(request, response);
            return;
        }

        Claims claims = jwtTokenUtil.getAllClaimsFromToken(authorizationS);
        //说明解析失败了
        if (claims == null) {
            throw new BadCredentialsException("token异常或已过期");
        }
        String authorization = claims.getSubject();
        TokenInfo tokenInfo = JSON.parseObject(authorization, TokenInfo.class);
        //通过loginType的区分,避免每次都查数据库
        if (LoginType.WX.getLoginType().equals(tokenInfo.getLoginType())) {
            User user = userService.loadUserByUserId(tokenInfo.getUserId());
            //用户不存在 todo

            BeesCommonAuthenticationToken authentication = new BeesCommonAuthenticationToken(JSON.toJSONString(user), "");
            //UserDetail user = userService.loadUserByUserId(tokenInfo.getUserId());
//                BeesCommonAuthenticationToken authentication = new BeesCommonAuthenticationToken(user.getUser().getWxOpenId(), "", user.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        chain.doFilter(request, response);
    }
}

JwtTokenUtil

@Component
public class JwtTokenUtil {

    private static final long JWT_EXPIRATION_TIME_MS = 86400000; // 1 day
    /**
    * 加密用的盐
    */
    @Value("${security.jwt.secretKey}")
    private String secretKey;

    // 生成JWT令牌
    public String generateToken(Integer loginType, Long userId) {
        TokenInfo tokenInfo = new TokenInfo();
        tokenInfo.setUserId(userId);
        tokenInfo.setLoginType(loginType);
        String subject = JSON.toJSONString(tokenInfo);
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION_TIME_MS))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    // 解析JWT令牌并获取claims
    public Claims getAllClaimsFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    // 从JWT令牌中提取用户名
    public String getUsernameFromToken(String token) {
        return getAllClaimsFromToken(token).getSubject();
    }

    // 验证JWT令牌是否有效
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    // 检查JWT令牌是否过期
    private Boolean isTokenExpired(String token) {
        final Date expiration = getAllClaimsFromToken(token).getExpiration();
        return expiration.before(new Date());
    }

    // 生成签名密钥
    private SecretKey getSigningKey() {
        byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

——————————————以上————————————————————
希望对各位有帮助。

参考文章:
SpringSecurity6解决requestMatchers().permitAll()后依然执行自定义过滤器的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/584334.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

maven多模块创建-安装配置

1、前提 许久没有写文章了&#xff0c;荒废了2年多的时间&#xff0c;在整理的时候&#xff0c;发现Maven还差一篇安装配置的文章&#xff0c;现在开始提笔完善它&#xff0c;参考&#xff1a;https://blog.csdn.net/m0_72803119/article/details/134634164。 —写于2024年4月…

有什么好用的足球数据分析工具,可以生成可靠的投注策略?

在寻找好用的足球数据分析工具以生成可靠的投注策略时&#xff0c;有几个值得考虑的选项。以下是一些工具和建议&#xff1a; 乐彩数据分析&#xff1a;这款工具以其精准的预测和高达70%以上的准确率而受到赞誉。它利用大数据算法进行预测&#xff0c;相比个人预测更加准确。此…

五一假期终于到了!是时候偷偷发力了!——早读(逆天打工人爬取热门微信文章解读)

狗子&#xff0c;别偷跑&#xff01;给我好好休息 引言Python 代码第一篇 洞见 无论在哪儿上班&#xff0c;冷漠是你最后的底线第二篇 人民日报要闻社会政策 结尾 我们不应该把休息 仅仅看作身体的需求 而敷衍了事 我们要把休息 看成一种机遇 停下工作 好好休息 并没有妨碍创造…

【数据结构】合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 Definition for singly-linked list.struct ListNode {int val;struct ListNode *next;};typedef struct ListNode ListNode; struct ListNode* mergeTwoLists(struct Lis…

LT6911UXE HDMI 2.0 至双端口 MIPI DSI/CSI,带音频 龙迅方案

1. 描述LT6911UXE 是一款高性能 HDMI2.0 至 MIPI DSI/CSI 转换器&#xff0c;适用于 VR、智能手机和显示应用。HDMI2.0 输入支持高达 6Gbps 的数据速率&#xff0c;可为4k60Hz视频提供足够的带宽。此外&#xff0c;数据解密还支持 HDCP2.3。对于 MIPI DSI / CSI 输出&#xff0…

rtl8188ftv debian linux 多架构移植方法

5 块包邮&#xff0c;挂到 x86_64 debian 12 虚拟机&#xff0c;实测下载能到 22Mbps&#xff0c;也可能就2Mbps&#xff0c;上传能到 40Mbps 关键词&#xff1a; rtl8xxxu、rtl8xxxu.ko、rtl8xxxu_8188f.c、mac80211.h、cfg80211.ko、sudo modinfo rtl8xxxu.ko | grep depen…

【Qt】error LNK2001: 无法解析的外部符号

参考&#xff1a;Qt/VS LNK2019/LNK2001&#xff1a;无法解析的外部符号_qt lnk2001无法解析的外部符号-CSDN博客 微软官方报错文档-链接器工具错误 LNK2019 __declspec error LNK2001: 无法解析的外部符号 "__declspec(dllimport) 原因 以这种为前缀的基本上跟库相关…

用Scrapy编写第一个入门项目(基础四件套:spider,pipeline,setting,items)

简介&#xff1a;scrapy是一个用于爬取网页并提取数据的应用框架&#xff0c;也可用于提取API数据 写在前面&#xff1a;只想看scrapy的童鞋子请跳过5-7直接step8&#xff09; step5&#xff0c;6是xpath和css入门&#xff0c;用于提取数据&#xff1b; step7是文件储存方式&…

SoundStream: 下一代的神经网络音频编解码器,实时压缩不牺牲音质

音频编解码技术的目标是&#xff0c;通过减少音频文件的大小来节省存储空间或减轻网络传输的负担。理想的情况下&#xff0c;即使音频被压缩&#xff0c;我们听到的声音与原版也应该没有任何区别。 过去&#xff0c;已经有不少编解码技术被开发出来&#xff0c;满足了这些需求…

【VS Code安装及远程服务器】(未完待续)

目录 一、Python 安装及设置1.1 Python安装1.2 Python设置 二、VScode 安装2.1 VScode安装2.2 中文界面设置及解决中文显示乱码问题2.2.1 中文界面设置2.2.2 解决中文显示乱码问题 2.3 VScode环境变量配置2.4 VScode添加到右键2.5 VScode终端&#xff0c;创建、激活虚拟环境&am…

Docker-Compose单机多容器应用编排与管理

前言 Docker Compose 作为 Docker 生态系统中的一个重要组件&#xff0c;为开发人员提供了一种简单而强大的方式来定义和运行多个容器化应用。本文将介绍 Docker Compose 的使用背景、优劣势以及利用 Docker Compose 简化应用程序的部署和管理。 目录 一、Docker Compose 简…

数据结构复习指导之串

文章目录 串 考纲内容 复习提示 1.串的定义和实现 1.1串的定义 1.2串的存储结构 1.2.1定长顺序存储表示 1.2.2堆分配存储表示 1.2.3块链存储表示 2.串的基本操作 拓展 知识回顾 串 考纲内容 字符串模式匹配 复习提示 本章是统考大纲第6章内容,采纳读者建议单独作为…

ActiveMQ 反序列化漏洞 (CVE-2015-5254)

一、漏洞描述 Apache ActiveMQ 是由美国阿帕奇&#xff08;Apache&#xff09;软件基金会开发的开源消息中间件&#xff0c;支持 Java 消息服务、集群、Spring 框架等。属于消息队列组件(消息队列组件&#xff1a;分布式系统中的重要组件&#xff0c;主要解决应用耦合、异步消息…

宽字符的来历:从ASCII到Unicode,C语言中的宽字符处理

目录 一、ASCII编码&#xff1a;字符世界的开篇 二、Unicode与宽字符的诞生 宽字符类型与宽字符串 三、C语言中的宽字符处理函数 四、宽字符与多字节字符 结语 在计算机科学的发展历程中&#xff0c;字符编码经历了从简单到复杂、从单一语言到全球多语种支持的演变过程。…

十大落地护眼灯有哪些?2024十大落地灯品牌排名

十大落地护眼灯有哪些&#xff1f;想要让孩子在舒适敞亮的光线下学习&#xff0c;不少家长都会给孩子选择入手落地灯&#xff0c;不过市面上却流传着落地灯品质恶劣的负面新闻。我是一名专业测评家居博主&#xff0c;终于搞清楚落地灯负面新闻的原因&#xff0c;其原因主要是因…

回顾python

回顾python 目录 回顾python 1.定义变量 2.分支控制结构 3.for循环 4.while 循环 5.类 面向对象 &#xff11;&#xff09;​方法的定义&#xff1a; &#xff12;&#xff09;类的定义&#xff1a; &#xff13;&#xff09;类的继承 1.定义变量 a23b"张三&quo…

【NOI-题解】1607. 两位数运算1020. 算算和是多少1029. 倒序输出一个四位整数1418. 求一个5位数的各个位之和1608. 三位数运算

文章目录 一、前言二、问题问题&#xff1a;1607. 两位数运算问题&#xff1a;1020. 算算和是多少问题&#xff1a;1029. 倒序输出一个四位整数问题&#xff1a;1418. 求一个5位数的各个位之和问题&#xff1a;1608. 三位数运算 三、感谢 一、前言 本章节主要讲解基本运算中的…

在线商城客服系统,多用户电商系统可API对接客服软件

在当今数字化时代&#xff0c;在线商城客服系统和多用户电商系统之间的无缝API对接已成为电商行业的重要趋势。这种整合为商家提供了更高效的客户服务和管理方式&#xff0c;提升了用户体验和业务效率。其中&#xff0c;商淘云电商客服系统作为一款强大的客服管理工具&#xff…

react props传参

props是父子传参的常用方法。 一、主要功能 1.传参 定义&#xff1a;父级组件向子级组件传递参数。 2.验证数据类型格式 定义&#xff1a;可以指定父组件传递过来数据为指定类型。 3.设置默认值 定义&#xff1a;在参数未使用时&#xff0c;直接默认为指定值。 二、实例代…

OpenSceneGraph

文章目录 关于 OpenSceneGraphScreenshots - OpenMW 关于 OpenSceneGraph 官网&#xff1a;https://openscenegraph.github.io/openscenegraph.io/github : https://github.com/openscenegraph/OpenSceneGraphClasses : https://podsvirov.github.io/osg/reference/opensceneg…