用API Key保护Spring Boot 接口的安全

news2024/12/30 2:24:43

1、概述

     安全性在REST API开发中扮演着重要的角色。一个不安全的REST API可以直接访问到后台系统中的敏感数据。因此,企业组织需要关注API安全性。

        Spring Security 提供了各种机制来保护我们的 REST API。其中之一是 API 密钥。API 密钥是客户端在调用 API 调用时提供的令牌。

       在本教程中,我们将讨论如何在Spring Security中实现基于API密钥的身份验证。

2、REST API Security

       Spring Security可以用来保护REST API的安全性。REST API是无状态的,因此不应该使用会话或cookie。相反,应该使用Basic authentication,API Keys,JWT或OAuth2-based tokens来确保其安全性。

2.1. Basic Authentication

      Basic authentication是一种简单的认证方案。客户端发送HTTP请求,其中包含Authorization标头的值为Basic base64_url编码的用户名:密码。Basic authentication仅在HTTPS / SSL等其他安全机制下才被认为是安全的。

2.2. OAuth2

     OAuth2是REST API安全的行业标准。它是一种开放的认证和授权标准,允许资源所有者通过访问令牌将授权委托给客户端,以获得对私有数据的访问权限。

2.3. API Keys

      一些REST API使用API密钥进行身份验证。API密钥是一个标记,用于向API客户端标识API,而无需引用实际用户。标记可以作为查询字符串或在请求头中发送。

3、用API Keys保护REST API

3.1  添加Maven 依赖

      让我们首先在我们的pom.xml中声明spring-boot-starter-security依赖关系:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

3.2 创建自定义过滤器(Filter)

实现思路是从请求头中获取API Key,然后使用我们的配置检查秘钥。在这种情况下,我们需要在Spring Security 配置类中添加一个自定义的Filter。

我们将从实现GenericFilterBean开始。GenericFilterBean是一个基于javax.servlet.Filter接口的简单Spring实现。

让我们创建AuthenticationFilter类:


public class AuthenticationFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws IOException, ServletException {
        try {
            Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } catch (Exception exp) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
            PrintWriter writer = httpResponse.getWriter();
            writer.print(exp.getMessage());
            writer.flush();
            writer.close();
        }

        filterChain.doFilter(request, response);
    }
}

我们只需要实现doFilter()方法,在这个方法中我们从请求头中获取API Key,并将生成的Authentication对象设置到当前的SecurityContext实例中。

然后请求被传递给其余的过滤器处理,接着转发给DispatcherServlet最后到达我们的控制器。

AuthenticationService类中,实现从Header中获取API Key并构造Authentication对象,代码如下:


public class AuthenticationService {
    private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY";
    private static final String AUTH_TOKEN = "Baeldung";

    public static Authentication getAuthentication(HttpServletRequest request) {
        String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME);

        if ((apiKey == null) || !apiKey.equals(AUTH_TOKEN)) {
            throw new BadCredentialsException("Invalid API Key");
        }

        return new ApiKeyAuthentication(apiKey, AuthorityUtils.NO_AUTHORITIES);
    }
}

在这里,我们检查请求头是否包含 API Key,如果为空 或者Key值不等于密钥,那么就抛出一个 BadCredentialsException。如果请求头包含 API Key,并且验证通过,则将密钥添加到安全上下文中,然后调用下一个安全过滤器。getAuthentication 方法非常简单,我们只是比较 API Key 头部和密钥是否相等。

为了构建 Authentication 对象,我们必须使用 Spring Security 为了标准身份验证而构建对象时使用的相同方法。所以,需要扩展 AbstractAuthenticationToken 类并手动触发身份验证。

3.3. 扩展AbstractAuthenticationToken

为了成功地实现我们应用的身份验证功能,我们需要将传入的API Key转换为AbstractAuthenticationToken类型的身份验证对象。AbstractAuthenticationToken类实现了Authentication接口,表示一个认证请求的主体和认证信息。

让我们创建ApiKeyAuthentication类:


public class ApiKeyAuthentication extends AbstractAuthenticationToken {
    private final String apiKey;

    public ApiKeyAuthentication(String apiKey,
        Collection<?extends GrantedAuthority> authorities) {
        super(authorities);
        this.apiKey = apiKey;
        setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return apiKey;
    }
}

ApiKeyAuthentication 类是类型为 AbstractAuthenticationToken 的对象,其中包含从 HTTP 请求中获取的 apiKey 信息。在构造方法中使用 setAuthenticated(true) 方法。因此,Authentication对象包含 apiKey 和authenticated字段:

3.4. Security Config

通过创建建一个SecurityFilterChain bean,可以通过编程方式把我们上面编写的自定义过滤器(Filter)进行注册。

我们需要在 HttpSecurity 实例上使用 addFilterBefore() 方法在 UsernamePasswordAuthenticationFilter 类之前添加 AuthenticationFilter

创建SecurityConfig 类:


@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf()
          .disable()
          .authorizeRequests()
          .antMatchers("/**")
          .authenticated()
          .and()
          .httpBasic()
          .and()
          .sessionManagement()
          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
          .and()
          .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

}
 

此外注意代码中我们吧绘画策略(session policy)设置为无状态(STATELESS),因为我们使用的是REST。

3.5. ResourceController

最后,我们创建ResourceController,实现一个Get请求  /home


@RestController
public class ResourceController {
    @GetMapping("/home")
    public String homeEndpoint() {
        return "Baeldung !";
    }
}

3.6. 禁用 Auto-Configuration


@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
public class ApiKeySecretAuthApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiKeySecretAuthApplication.class, args);
    }
}
 

4. 测试

我们先不提供API Key进行测试

curl --location --request GET 'http://localhost:8080/home'

返回 401 未经授权错误

请求头中加上API Key后,再次请求

curl --location --request GET 'http://localhost:8080/home' \
--header 'X-API-KEY: Baeldung'

请求返回状态200

代码:https://github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-boot-4

 

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

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

相关文章

DAY 79 云原生DOCKER的基本原理及镜像管理

Docker概述 云计算涌现出很多改变传统IT架构和运维方式的新技术&#xff0c;比如虚拟机、容器、微服务、Serverless&#xff08;无服务&#xff09;&#xff0c;无论这些技术应用在哪些场景&#xff0c;降低成本、提升效率是云服务永恒的主题。 1.运行物理机&#xff0c;也称…

【历史上的今天】6 月 26 日:EDSAC 计算机之父诞生;B 站成立;Skype 创始人出生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 6 月 26 日&#xff0c;在 1911 年的今天&#xff0c;计算机先驱弗雷德里克威廉姆斯&#xff08;Frederic Williams&#xff09;出生。威廉姆斯是一位英国工程…

1: BDF(Bus,Device,Function)

目录 1.概述 2.BUS&#xff1a;总线号 3.Device&#xff1a;设备号 4.Function&#xff1a;功能号 1.概述 PCIe总线中的每一个功能都有一个唯一的标识符与之对应。这个标识符就是BDF&#xff08;Bus&#xff0c;Device&#xff0c;Function&#xff09; 2.BUS&#xff1a;总…

如何在 Spring Boot 中使用 WebMvc

如何在 Spring Boot 中使用 WebMvc 引言 Spring Boot 是一个快速、简单的开发框架&#xff0c;可以帮助我们快速地搭建一个基于 Spring 的 Web 应用程序。在 Spring Boot 中&#xff0c;我们可以使用 WebMvc 来构建 Web 应用程序。WebMvc 是 Spring 框架中的一个模块&#xf…

【案例实战】SpringBoot整合Redisson实现RedLock分布式锁同步

思考&#xff1a;生产环境下Redis集群环境&#xff0c;怎么保证锁的同步&#xff1f; 我们先来回顾一下分布式锁的作用&#xff1a;就是保证同一时间只有一个客户端可以对共享资源进行操作。 当我们集群环境部署的时候&#xff0c;假如节点一在主节点获取分布式锁成功。Redis…

【微服务架构模式】构建应用程序的顶级微服务设计模式

在当今市场上&#xff0c;微服务已成为构建应用程序的首选解决方案。众所周知&#xff0c;它们可以解决各种挑战&#xff0c;但是&#xff0c;熟练的专业人员在使用此架构时经常面临挑战。因此&#xff0c;相反&#xff0c;开发人员可以探索这些问题中的常见模式&#xff0c;并…

简单的Image Picker:使用Jetpack Compose无需权限申请

简单的Image Picker&#xff1a;使用Jetpack Compose无需权限申请 学习如何在Android应用中轻松选择、存储和加载本地图像&#xff0c;无需繁琐的权限处理。 作为一名Android开发者&#xff0c;我知道在应用中实现本地图像选择时处理权限可能会让人感到沮丧。这就是为什么我想…

Juc04_阻塞队列概述、方法、实现类、Linked和Array区别、注意事项

文章目录 ①. 什么是阻塞队列②. BlockingQueue的主要方法③. BlockingQueue的实现类④. Linked和Array区别⑤. 不推荐使用快捷的线程池 ①. 什么是阻塞队列 ①.阻塞队列:从名字可以看出,它也是队列的一种,那么它肯定是一个先进先出FIFO的数据结构。与普通队列不同的是,他支持两…

Flask新手教程

Flask简介 Flask是一个轻量级的可定制框架&#xff0c;使用Python语言编写&#xff0c;较其他同类型框架更为灵活、轻便、安全且容易上手。 Flask 可以很好地结合MVC模式进行开发&#xff0c;开发人员分工合作&#xff0c;小型团队在短时间内就可以完成功能丰富的中小型网站或…

正点原子uboot分析

知识点 为终端不输出command line&#xff1a;终端输入如果变量quiet为空的话&#xff0c;整个命令都会输出。 如果变量 quiet为“ quiet_”的话&#xff0c;仅输出短版本。 如果变量 quiet为“ silent_”的话&#xff0c;整个命令都不会输出。sinclude&#xff1a;读取的文件…

Java通过JNI调用dll动态库详细步骤

目录 目标具体示例1、编写java代码&#xff1a;定义native接口2、根据java编写的native接口生成.h头文件3、使用Visual Studio编写c代码实现头文件接口并生成dll文件4、将生成的jni.dll文件放入jdk bin下5、编写java测试类&#xff0c;调用dll 附&#xff1a;问题java测试类执行…

上门家教app小程序源码开发的前景如何?

随着我国生活水平的提高&#xff0c;教育方面的问题也越来越受到家长们的重视&#xff0c;很多家庭都开始通过家教以及辅导班等方式增强学生的学业知识&#xff0c;因此家教app开发是拥有很大一部分用户市场的。那么家教app开发主要适合什么行业呢&#xff1f; 上门家教服务或…

Zabbix-客户端部署全过程

本文已收录于专栏 《中间件合集》 目录 概念说明什么是Zabbix 功能介绍配置过程1.在linux下的admin文件夹下创建zabbix文件夹2.把agent端压缩包放置到home/admin/zabbix路径下3.解压安装包4.创建zabbix日志文件5.进入到conf文件夹下&#xff0c;修改配置文件&#xff0c;与serv…

Linux系统下网络性能监控指令(iftop、nload)

文章目录 iftop参数快捷键iftop界面说明&#xff1a; nload参数示例 iftop 参数 -i 设定监测的网卡&#xff0c;如&#xff1a;# iftop -i eth1 -B 以bytes为单位显示流量(默认是bits)&#xff0c;如&#xff1a;# iftop -B -n 使host信息默认直接都显示IP&#xff0c;如&…

今天给大家安利几款非常好用的文件迁移工具

文件迁移是我们平时经常需要处理的问题之一。在日常生活中&#xff0c;我们可能需要将一些文件从电脑上的一个位置迁移到另一个位置&#xff0c;或者将文件转移到外部硬盘或云端存储空间。而为了更加高效地完成这项任务&#xff0c;使用一款好用的文件迁移工具显得尤为重要。今…

基于smardaten无代码开发解决光伏电站项目

文章目录 前言一、事故背景二、解决方案三、工具加持四、配置要点解析1、光伏导航菜单搭建2、运行监控组件布局3、员工填报表单创建4、数据接入清洗5、复杂数据展示5、地图大屏组装6、定制页面集成 五、挖掘更多惊喜1、模拟数据生成2、智能分析结果3、草图智能识别4、日期排班设…

el-dialog设置滚动条不生效记录【草稿版,待优化】

目录 前言一、scrollTop是什么&#xff1f;二、解决步骤总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 技术栈 element-plus vue3 js 记录el-dialog的弹窗中&#xff0c;通过js控制滚动条不生效。 我这次的需求是这样的&#xff0c;点击某个按钮…

SSM-Spring项目使用Tomcat:7插件运行项目注意事项

文章目录 配置Tomcat:7插件&#xff08;注意Path&#xff09;运行tomcat:7第一种方式第二种方式 测试路径&#xff08;注意&#xff09;正确请求方式&#xff08;注意路径&#xff09; 配置Tomcat:7插件&#xff08;注意Path&#xff09; 作为演示&#xff0c;我这里特别配置了…

中学生台灯怎么选比较好?精选真正适合中学生的台灯!

现在孩子的近视率很高&#xff0c;尤其是儿童青少年居多&#xff0c;从上了小学开始作业就变多了&#xff0c;经常挑起夜灯学习的&#xff0c;而中学生负担则更重。家长重视教育质量的同时也要注意孩子学习时的光线适合学习吗&#xff1f;用眼过度和不适合的光源容易导致近视&a…

Sangfor华东天勇战队:jeecg-boot登录分析

realkey获取 7evs1680077379806 可以看到代码逻辑如下&#xff0c;md5(小写验证码checkKey)&#xff0c;这里checkKey是时间戳 然后继续往下看 这里checkcode取值为空&#xff0c;这是为什么呢&#xff0c;是因为redisUtil中没有获得&#xff0c;登录的时候会存到redis中&a…