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

news2024/9/22 7:38:24

背景:通过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也能访问成功了。
在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

获取未来的5分钟整点时间05,10,15,20,25...

比如预约网约车的时候&#xff0c;是按5分钟的整点时间 GetMapping("/getFiveNextTime")public String fiveNextTime(RequestParam(defaultValue "0") Integer interval) {Calendar calendar Calendar.getInstance();calendar.add(Calendar.MINUTE, (5 …

MySQL进阶45讲【13】为什么表数据删掉一半,表文件大小不变?

1 前言 有些小伙伴在删数据库数据时&#xff0c;会产生一个疑问&#xff0c;我的数据库占用空间大&#xff0c;我把一个最大的表删掉了一半的数据&#xff0c;怎么表文件的大小还是没变&#xff1f; 那么这篇文章&#xff0c;就介绍一下数据库表的空间回收&#xff0c;看看如…

以太网帧格式及ARP协议简介

在以太网中&#xff0c;一个主机和另一个主机进行通信&#xff0c;必须要知道目的主机的MAC地址&#xff08;物理地址&#xff09;&#xff0c;只要知道目的主机的IP地址&#xff0c;就可以通过ARP协议获取目的主机的MAC地址。 1、ARP协议简介 ARP&#xff08;Address Resolut…

leetcode刷题(剑指offer) 297.二叉树的序列化和反序列化

297.二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xff0c;采取相反方式重构得到原数据。 请设计一个算法来实现…

C语言-2

自定义类型 基本认识 /*引入&#xff1a;学生&#xff1a;姓名&#xff0c;学号&#xff0c;年龄&#xff0c;成绩请为学生们专门定制一个类型&#xff08;创造一个类型&#xff09;结构体格式&#xff1a;struct 标识符 // 标识符即自定义类型的名称{成员; // 自己设置…

H5 自适应超人背景引导页源码

H5 自适应超人背景引导页源码 源码介绍&#xff1a;一款自适应引导页源码&#xff0c;带超人背景。有四个跳转按钮。 下载地址&#xff1a; https://www.changyouzuhao.cn/11608.html

深度学习实战 | 卷积神经网络LeNet手写数字识别(带手写板GUI界面)

引言 在深度学习领域&#xff0c;卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种广泛应用于图像识别任务的神经网络结构。LeNet是一种经典的CNN结构&#xff0c;被广泛应用于基础的图像分类任务。本文将介绍如何使用LeNet卷积神经网络实现手写…

yum命令下载出现Failed to synchronize cache for repo ‘AppStream‘, ignoring this repo.

修改下面的配置文件 问题&#xff1a; cd /etc/yum.repos.d 修改下面四个文件 vim CentOS-Base.repo vim CentOS-AppStream.repo vim CentOS-Extras.repo vim CentOS-PowerTools.repo测试yum是否正常 yum -y install wget

极速搭建幻兽帕鲁私服,叫上好友春节假期一起联机畅玩帕鲁

文章目录 前言幻兽帕鲁私服详细部署教程查看服务器开始游戏自定义游戏参数配置 前言 行业资讯 《幻兽帕鲁》的火爆对开发商 Pocketpair 来说&#xff0c;代价是巨大的。该游戏的成功让首席执行官沟部拓郎最近在推特上表示&#xff0c;他可能因服务器运营费用而面临破产。据他透…

[文本挖掘和知识发现] 03.基于大连理工情感词典的情感分析和情绪计算

作者于2023年8月新开专栏——《文本挖掘和知识发现》&#xff0c;主要结合Python、大数据分析和人工智能分享文本挖掘、知识图谱、知识发现、图书情报等内容。这些内容也是作者《文本挖掘和知识发现&#xff08;Python版&#xff09;》书籍的部分介绍&#xff0c;本书预计2024年…

vscode 括号 python函数括号补全

解决方法 在setting.json中添加 “python.analysis.completeFunctionParens”: true 打开设置&#xff1b; 点击图中按钮打开setting.json文件 添加 “python.analysis.completeFunctionParens”: true

管理类联考-复试-全流程演练-导航页

文章目录 整体第一步&#xff1a;学校导师两手抓——知己知彼是关键学校校训历史 导师 第二步&#xff1a;面试问题提前背——押题助沟通自我介绍——出现概率&#xff1a;100%为什么选择这个专业&#xff1f;今后如何打算&#xff1f;你认为自己本科专业和现在所考的专业有什么…

嵌入式linux移植篇之Uboot

什么是bootloader&#xff1f; 芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设&#xff0c;然后将Linux内核从flash(NAND&#xff0c;NOR FLASH&#xff0c;SD&#xff0c;MMC 等)拷贝到 DDR 中&#xff0c;最后启动 Linux 内核。当然了&#…

分享5款让人眼前一亮的软件

​ 让你眼前一亮的软件&#xff0c;不一定是市面上最流行的。今天&#xff0c;我将推荐给你五款非常小众&#xff0c;但是十分好用的软件。 1.自动化脚本——AutoIt ​ AutoIt是一款自动化脚本软件&#xff0c;可以让你编写和运行一些简单的程序&#xff0c;实现一些重复性或…

【C++】拷贝构造函数和赋值运算符重载详解

目录 拷贝构造函数 概念 特征 赋值运算符重载 运算符重载 赋值运算符重载 ​编辑前置和后置重载 ⭐拷贝构造函数 ⭐概念 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存 在的类类型对象创建新…

Apache Doris 整合 FLINK CDC + Iceberg 构建实时湖仓一体的联邦查询

1概况 本文展示如何使用 Flink CDC Iceberg Doris 构建实时湖仓一体的联邦查询分析&#xff0c;Doris 1.1版本提供了Iceberg的支持&#xff0c;本文主要展示Doris和Iceberg怎么使用&#xff0c;大家按照步骤可以一步步完成。完整体验整个搭建操作的过程。 2系统架构 我们整…

【AI绘画+Midjourney平替】Fooocus:图像生成、修改软件(Controlnet原作者重新设计的UI+Windows一键部署)

代码&#xff1a;https://github.com/lllyasviel/Fooocus windows一键启动包下载&#xff1a;https://github.com/lllyasviel/Fooocus/releases/download/release/Fooocus_win64_2-1-831.7z B站视频教程&#xff1a;AI绘画入门神器&#xff1a;Fooocus | 简化SD流程&#xff0c…

2024年Java面试题大全 面试题附答案详解,BTA内部面试题

基础篇 1、 Java语言有哪些特点 1、简单易学、有丰富的类库 2、面向对象&#xff08;Java最重要的特性&#xff0c;让程序耦合度更低&#xff0c;内聚性更高&#xff09; 阿里内部资料 基本类型 大小&#xff08;字节&#xff09; 默认值 封装类 6、Java自动装箱与拆箱 装箱就是…

Django视图

一、返回错误响应 返回错误的3种方式: 中间件设置的属性: Django的contrib应用程序中包含的一些中间件在请求中设置了属性。如果在请求中看不到该属性,请确保使用了相应的中间件类MIDDLEWARE 返回 HttpResponseNotFound返回 HttpResponse 设置 status 状态码返回 Http404状…