SpringBoot security 安全认证(三)——自定义注解实现接口放行配置

背景:通过Security实现了安全管理,可以配置哪些接口可以无token直接访问。但一个麻烦就是每增加一个匿名访问接口时都要去修改SecurityConfig配置,从程序设计上讲是不太让人接受的。
本节内容:即是解决以上问题,增加一个匿名访问接口,但不要去修改SecurityConfig配置。在需要匿名的接口上添加注解,系统启动时扫描带注解的接口,SecurityConfig配置时,读取这些接口,即可完成自动配置匿名访问了。

主要内容

1、定义注解Anonymous;
2、注解解析器,解析出所有带注解的接口;
3、改造SecurityConfig,将匿名接口设置允许匿名访问;

1、注解Anonymous

注解支持类和方法名

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

2、注解解析器

获取包含Anonymous 注解的方法和类,解析出各接口url,放置到List中。初始化后,springBean中将有一个PermitAllUrlProperties的对象。

/**
 * 设置Anonymous注解允许匿名访问的url
 *
 */
@Configuration
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware
{
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");

    private ApplicationContext applicationContext;

    private List<String> urls = new ArrayList<>();

    public String ASTERISK = "*";

    @Override
    public void afterPropertiesSet()
    {
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();

        map.keySet().forEach(info -> {
            HandlerMethod handlerMethod = map.get(info);

            // 获取方法上边的注解 替代path variable 为 *
            Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
            Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
                    .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));

            // 获取类上边的注解, 替代path variable 为 *
            Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
            Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns())
                    .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
        });
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException
    {
        this.applicationContext = context;
    }

    public List<String> getUrls()
    {
        return urls;
    }

    public void setUrls(List<String> urls)
    {
        this.urls = urls;
    }
}

3、修改SecurityConfig

将PermitAllUrlProperties对象注入SecurityConfig中,然后获取需要匿名访问的接口,完成对每个接口的匿名访问配置。


/**
 * spring security配置
 *
 * @author
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 认证失败处理类
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * 退出处理类
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * token认证过滤器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    /**
     * 跨域过滤器
     */
    @Autowired
    private CorsFilter corsFilter;

    /**
     * 允许匿名访问的地址
     */
    @Autowired
    private PermitAllUrlProperties permitAllUrl;

    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // 注解标记允许匿名访问的url
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
        permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());

        httpSecurity
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 禁用HTTP响应标头
                .headers().cacheControl().disable().and()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                .antMatchers("/swagger-ui.html","/login", "/register", "/captchaImage").permitAll()
                // 静态资源,可匿名访问
                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/**/attachment/**", "/profile/**").permitAll()
                .antMatchers("/swagger-ui.html" ,"/v2/api-docs","/swagger-resources", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();
        // 添加Logout filter
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }

    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

匿名接口配置

在list()方法上添加@Anonymous的注解,标志为匿名访问。

@RestController
@RequestMapping("/users")
@Api(tags = "用户 API 接口")
public class UserController {

    @Autowired
    UserService userService;

    @Anonymous //标该方法可以匿名访问
    @GetMapping("/list")
    @ApiOperation(value = "查询用户列表", notes = "目前仅仅是作为测试,所以返回用户全列表")
    public AjaxResult list() {
        // 返回列表
        return AjaxResult.success(userService.selectUser());
    }
}

接口匿名访问验证:即使不带token也能访问成功了。
在这里插入图片描述

简单几行代码就解决了看似复杂的问题。不得不感叹注解的强大!

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

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

相关文章

SpringBoot整合Flowable最新教程(二)启动流程

介绍 文章主要从SpringBoot整合Flowable讲起&#xff0c;关于Flowable是什么&#xff1f;数据库表解读以及操作的Service请查看SpringBoot整合Flowable最新教程&#xff08;一&#xff09;&#xff1b;   其他说明&#xff1a;Springboot版本是2.6.13&#xff0c;java版本是1…

python2.7安装和添加环境变量

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、python的下载地址二、安装步骤1.双击安装包运行安装2.添加环境变量 三、验证是否安装成功总结 前言 最近要用到python,下载安装成功&#xff0c;添加环境变…

Java 数据结构 二叉树(二)红黑树

目录 数据结构图-树 简介 规则 旋转 重新着色 红黑树构建过程 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的漩涡&#xff0c;忘记了停下脚步&#xf…

华清远见嵌入式学习——春节作业——2.4日

作业要求&#xff1a; 编写程序实现二叉树的创建&#xff0c;三种遍历自己销毁 作业答案&#xff1a; 作业代码截图 作业代码效果图 作业代码 #include "myhead.h"// 定义二叉树节点结构体 struct Tree {int value; //编号(值)struct Tree* left; //左子树stru…

9隐藏登录用户(无需接触磁盘即可修改文件内容)_Linux_Rootkit.md

Xcellerator 密码学Linux其他逆向工程 文章目录 [Linux Rootkit 第 9 部分&#xff1a;隐藏登录用户&#xff08;无需接触磁盘即可修改文件内容&#xff09;](https://xcellerator.github.io/posts/linux_rootkits_09/)终端设备UTMP用户空间工具如何解析 UTMP&#xff1f;挂钩…

如何实现冻干机和产品全生命周期的验证和监测?

为什么冻干需要工艺优化和合规性 冻干是制药和生物技术产品的关键工艺&#xff0c;需要精确控制关键的温度和压力参数。通过遵守 GMP 和 FDA 合规性等监管准则&#xff0c;您可以生产出更高质量的产品&#xff0c;避免不必要的浪费&#xff0c;并缩短产品上市时间。 要想在冻干…

[linux] kolla-ansible 部署的openstack 修改mariadb默认端口号

kolla-ansible 部署前修改global.yml #freezer_database_backend: "mariadb" database_port: 9306 mariadb_port: 9306如果已经部署成功&#xff0c;直接修改配置文件里的端口号重启是没有用的&#xff0c;怀疑内部做了缓存&#xff0c;查看openstack 使用的memcach…

零基础学编程,从入门到精通,中文编程工具下载,时间选择构件用法

零基础学编程&#xff0c;从入门到精通&#xff0c;中文编程工具下载&#xff0c;时间选择构件用法 一、前言 编程工具下载及源码文件下载路径 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例源码文件下载可以点击最下方官网卡片——软件…

Unity类银河恶魔城学习记录1-11 PlayerPrimaryAttack P38

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Player.cs using System.Collections; using System.Collections.Generic…

基于spring boot实现邮箱发送和邮箱验证

目录 一、邮箱发送实现1. 开通邮箱服务2. 添加邮箱依赖3.添加配置4.添加邮箱通用类5. 测试类 二、邮箱验证实现1.添加依赖2. 添加配置3.添加controller4. 测试 项目地址: https://gitee.com/nssnail/springboot-email 一、邮箱发送实现 1. 开通邮箱服务 使用qq邮箱、163邮箱都…

《计算机网络简易速速上手小册》第10章:未来网络技术趋势(2024 最新版)

文章目录 10.1 边缘计算与网络设计 - 未来网络的速度与激情10.1.1 基础知识10.1.2 重点案例&#xff1a;使用 Python 实现边缘计算的实时视频分析准备工作Python 脚本示例 10.1.3 拓展案例1&#xff1a;智能交通系统Python 脚本示例 - 边缘计算设备上的交通流量分析 10.1.4 拓展…

vcruntime140_1.dll 文件缺失的解决方法,简单的修复方法分享

遇到 vcruntime140_1.dll 文件缺失是一种常见的问题&#xff0c;但无需过于担心&#xff0c;因为这类问题通常有多种解决办法。接下来&#xff0c;我会为大家详细介绍如何处理 vcruntime140_1.dll 文件缺失的情况&#xff0c;以及在解决过程中应该注意的重要事项。 一.vcrunti…

Python flask 模板详解

文章目录 1 概述1.1 模板简介1.2 templates 文件1.3 简单应用 2 模板语法2.1 for 循环2.2 if 判断 3 模板的继承3.1 格式要求3.2 实现示例3.3 复用父模板的内容&#xff1a;super 1 概述 1.1 模板简介 定义&#xff1a;定义好的 html 文件&#xff0c;用于快速开发 web 页面J…

04-OpenFeign-请求超时机制

基于 2021.0.1版本 具体有以下几种超时参数设置 以下仅限2021.0.1 以前的版本&#xff0c;高于该版本的spring cloud 已修复该问题 1、默认的超时时间 默认不生效 连接超时时间10秒、读超时时间60秒&#xff0c;源码在feign.Request.Options#Options()这个方法中 2、open…

在Linux中如何理解页表和进程地址

1、进程地址是进程读取资源的窗口 2、页表决定了进程真实拥有的资源情况 3、合理的对进程地址空间页表进行资源划分&#xff0c;就可以对进程的资源进行分类 这个过程应该如何去理解呢请看下面的图 我们知道程序被加载到进程中&#xff0c;会产生相应的PCB&#xff0c;并且…

【JavaScript 漫游】【006】数据类型 array

文章简介 本文为【JavaScript 漫游】专栏的第 006 篇文章&#xff0c;记录笔者在了解 JS 数据类型 array 中摘录的知识点。 数组的本质是对象属组的 length 属性for ... in 循环和数组的遍历数组的空位类数组对象 除了上述 5 个重要知识点&#xff0c;学习数组更为重要的是掌…

CSS transition(过渡效果)详解并附带示例

CSS过渡效果&#xff08;CSS transitions&#xff09;是一种在元素属性值发生变化时&#xff0c;通过指定过渡效果来实现平滑的动画效果的方法。通过定义起始状态和结束状态之间的过渡属性&#xff0c;可以使元素的变化更加流畅和可视化。 过渡效果的基本语法如下&#xff1a;…

使用潜在向量进行检测、屏蔽和重建以进行遮挡的面部表情识别

Latent-OFER: Detect, Mask, and Reconstruct with Latent Vectors for Occluded Facial Expression Recognition 一、创新点 &#xff08;1&#xff09;提出了一种与表情相关的特征提取器&#xff0c;它使用空间注意力为特定的面部特征分配更高的权重&#xff0c;从而使我们能…

前端开发中不同语言【react-i18next】

目录 查看并设置语言 单页面&#xff1a;html lang ​编辑 浏览器 自定义翻译&#xff1a;react-i18next 设置 模块&#xff1a;staticData.ts 散(重复利用)&#xff1a;命名空间.json 应用 准备 html标签 查看并设置语言 单页面&#xff1a;html lang 英语: <…

RflySim | 定点位置控制器设计实验三

RflySim| 定点位置控制器设计实验三 01 分析实验 1.调节PD控制器的相关参数改善系统控制性能&#xff0c;并记录超调量和调节时间&#xff0c;得到满意的参数。 2.在得到满意参数后&#xff0c;对系统进行扫频以绘制Bod图&#xff0c;观察系统幅频响应、相频响应曲线&#xf…