阿里云开发者社区

电脑版
提示:原网页已由神马搜索转码, 内容由developer.aliyun.com提供.

第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)

2024-04-19148
版权
版权声明:
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议》和 《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
简介:第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)

9526b1d6ced59dd11678343f8411959.png

7.1 保护 RESTAPI

在数字化时代,REST API是现代Web应用和微服务架构中数据交互的关键组成部分。然而,随着它们的普及和重要性的增加,保护这些API免受恶意攻击变得尤为重要。本节将探讨保护REST API的基础知识和实用案例。

7.1.1 基础知识详解

在构建和维护REST API时,安全性是一个不容忽视的要素。REST API作为应用程序与外界交互的接口,常常面临着各种安全威胁,包括但不限于身份盗窃、数据泄露、服务拒绝攻击等。因此,采取有效的安全措施保护REST API是至关重要的。以下是保护REST API时需掌握的基础知识。

身份验证 (Authentication)

  • 定义:确定请求者的身份,确保只有合法用户可以访问API。
  • 方法
  • 基本认证:通过HTTP头传递用户名和密码的简单认证方法,需要使用HTTPS来避免凭证泄露。
  • 令牌认证:如JWT,通过签名的令牌确认用户身份,支持无状态认证。
  • OAuth/OAuth2:为第三方应用提供限制的访问权限,而无需暴露用户的凭证。

授权 (Authorization)

  • 定义:确定已认证的用户可以执行的操作或访问的数据。
  • 实现方式
  • 角色基础的访问控制(RBAC):根据用户的角色来决定其权限。
  • 属性基础的访问控制(ABAC):根据属性(用户属性、资源属性和环境属性)来动态决定访问权限。

传输安全 (Transport Security)

  • HTTPS:使用SSL/TLS加密HTTP请求和响应,防止数据在传输过程中被截获或篡改。
  • HSTS(HTTP Strict Transport Security):强制客户端(如浏览器)使用HTTPS与服务器建立连接。

数据保护

  • 数据加密:对敏感数据进行加密处理,保护存储在服务器上或传输过程中的数据。
  • 数据脱敏:在公开的响应中避免直接展示敏感数据,如用户ID、电子邮件地址等。

输入验证

  • 目的:防止恶意输入导致的安全漏洞,如SQL注入、XSS攻击。
  • 实践:对所有输入数据进行验证,拒绝不符合预期格式的请求。

错误处理

  • 优雅处理:错误信息应足够通用,避免泄露敏感信息或系统细节。
  • 日志记录:记录错误日志,但避免在日志中记录敏感信息。

限制与节流 (Rate Limiting and Throttling)

  • 目的:防止API滥用,保护后端服务不受恶意攻击或过载。
  • 实现:限制来自单一来源的请求频率,当达到限制时返回适当的HTTP状态码。

通过这些基础知识的详解,我们可以看到保护REST API涉及到多个方面,包括但不限于身份验证、授权、传输安全、数据保护和输入验证等。正确实施这些安全措施,可以有效提高API的安全性,保护用户数据和服务的稳定性。

7.1.2 重点案例:使用 JWT 进行身份验证和授权

JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在双方之间安全地传输信息作为 JSON 对象。由于其紧凑和自包含的特性,JWT 非常适合用于 REST API 的身份验证和授权。以下案例将引导你实现 JWT 在 Spring Boot 应用中的身份验证和授权。

案例 Demo

步骤 1: 引入 JWT 库依赖

首先,在 Spring Boot 项目的pom.xml中添加对 JWT 库的依赖。这里我们使用jjwt库作为示例:

<dependency>    <groupId>io.jsonwebtoken</groupId>    <artifactId>jjwt</artifactId>    <version>0.9.1</version></dependency>

步骤 2: 创建JWT工具类

创建一个JWT工具类JwtUtil,用于生成和验证 JWT 令牌:

import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.springframework.stereotype.Component;import java.util.Date;import java.util.function.Function;@Componentpublic class JwtUtil {    private String secret = "yourSecretKey"; // 用于签名的密钥    public String extractUsername(String token) {        return extractClaim(token, Claims::getSubject);    }    public Date extractExpiration(String token) {        return extractClaim(token, Claims::getExpiration);    }    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {        final Claims claims = extractAllClaims(token);        return claimsResolver.apply(claims);    }    private Claims extractAllClaims(String token) {        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();    }    private Boolean isTokenExpired(String token) {        return extractExpiration(token).before(new Date());    }    public String generateToken(String username) {        return Jwts.builder().setSubject(username)                .setIssuedAt(new Date(System.currentTimeMillis()))                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时有效期                .signWith(SignatureAlgorithm.HS256, secret).compact();    }    public Boolean validateToken(String token, String username) {        final String tokenUsername = extractUsername(token);        return (username.equals(tokenUsername) && !isTokenExpired(token));    }}

步骤 3: 实现 JWT 请求过滤器

创建JwtRequestFilter类,它将在每次请求时检查 JWT 令牌的有效性:

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class JwtRequestFilter extends OncePerRequestFilter {    @Autowired    private MyUserDetailsService userDetailsService;    @Autowired    private JwtUtil jwtUtil;    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)            throws ServletException, IOException {                final String authorizationHeader = request.getHeader("Authorization");        String username = null;        String jwt = null;        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {            jwt = authorizationHeader.substring(7);            username = jwtUtil.extractUsername(jwt);        }        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);            if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {                UsernamePasswordAuthenticationToken authToken =                         new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));                SecurityContextHolder.getContext().setAuthentication(authToken);            }        }        chain.doFilter(request, response);    }}

步骤 4: 配置 Spring Security

最后,在 Spring Security 配置中注册JwtRequestFilter

import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private JwtRequestFilter jwtRequestFilter;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.csrf().disable()                .authorizeRequests().antMatchers("/authenticate").permitAll()                .anyRequest().authenticated()                .and().sessionManagement()                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);    }}

通过这些步骤,你的 Spring Boot 应用现在能够利用 JWT 进行身份验证和授权,从而保护 REST API 免受未授权访问。记得保密你的 JWT 密钥,并定期更新以维护系统安全。

7.1.3 拓展案例 1:API 密钥认证

API 密钥认证是一种简单但有效的安全措施,用于控制对 REST API 的访问。它适用于服务到服务的通信,其中一个服务需要验证另一个服务的请求。以下案例演示了如何在 Spring Boot 应用中实现 API 密钥认证。

案例 Demo

步骤 1: 定义 API 密钥存储

首先,假设我们有一个简单的方式来存储和验证 API 密钥。在实际应用中,这些密钥可能会存储在数据库或配置文件中。这里我们使用一个简单的 Map 模拟。

import org.springframework.stereotype.Component;import java.util.HashMap;import java.util.Map;@Componentpublic class ApiKeyStore {    private final Map<String, String> apiKeys = new HashMap<>();    public ApiKeyStore() {        // 初始化一些API密钥,实际应用中应该从安全的地方加载        apiKeys.put("service1", "key-123");        apiKeys.put("service2", "key-456");    }    public boolean validateKey(String serviceId, String apiKey) {        return apiKey.equals(apiKeys.get(serviceId));    }}

步骤 2: 实现 API 密钥认证过滤器

创建ApiKeyAuthenticationFilter类,该过滤器负责拦截请求并验证 API 密钥。

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class ApiKeyAuthenticationFilter extends OncePerRequestFilter {    @Autowired    private ApiKeyStore apiKeyStore;    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)            throws ServletException, IOException {                String serviceId = request.getHeader("Service-Id");        String apiKey = request.getHeader("API-Key");        if (serviceId == null || apiKey == null || !apiKeyStore.validateKey(serviceId, apiKey)) {            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API Key");            return;        }        chain.doFilter(request, response);    }}

步骤 3: 在Spring Security 配置中注册 API 密钥认证过滤器

接下来,需要在 Spring Security 配置中添加ApiKeyAuthenticationFilter

import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.context.annotation.Bean;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Bean    public ApiKeyAuthenticationFilter apiKeyAuthenticationFilter() {        return new ApiKeyAuthenticationFilter();    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http            .addFilterBefore(apiKeyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)            .authorizeRequests()                .anyRequest().authenticated()            .and()            .csrf().disable();    }}

通过以上步骤,Spring Boot 应用现在能够使用 API 密钥进行简单的身份验证。任何未提供有效 API 密钥的请求都将被拒绝访问。

测试API密钥认证

启动应用并尝试发送请求到受保护的端点,确保在请求头中包含有效的Service-IdAPI-Key。如果密钥验证失败,应收到 HTTP 401 Unauthorized 错误。

这种 API 密钥认证方法虽然简单,但在某些场景下非常有效,尤其是在服务对服务的通信中。记得保护好你的 API 密钥,避免泄露。


第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(中)+https://developer.aliyun.com/article/1487156

目录
相关文章
|
5天前
|
人工智能安全API
API应用安全风险倍增,F5助企业赢得关键安全挑战
API应用安全风险倍增,F5助企业赢得关键安全挑战
261111
|
18天前
|
安全JavaAPI
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
|
19天前
|
安全JavaAPI
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
|
20天前
|
缓存Java应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
|
20天前
|
Cloud Native安全Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
|
25天前
|
Java微服务Spring
驾驭复杂性:Spring Cloud在微服务构建中的决胜法则
【8月更文挑战第31天】Spring Cloud是在Spring Framework基础上打造的微服务解决方案,提供服务发现、配置管理、消息路由等功能,适用于构建复杂的微服务架构。本文介绍如何利用Spring Cloud搭建微服务,包括Eureka服务发现、Config Server配置管理和Zuul API网关等组件的配置与使用。通过Spring Cloud,可实现快速开发、自动化配置,并提升系统的伸缩性和容错性,尽管仍需面对分布式事务等挑战,但其强大的社区支持有助于解决问题。
|
25天前
|
测试技术JavaSpring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
|
25天前
|
JavaSpringAPI
Spring框架与GraphQL的史诗级碰撞:颠覆传统,重塑API开发的未来传奇!
【8月更文挑战第31天】《Spring框架与GraphQL:构建现代API》介绍了如何结合Spring框架与GraphQL构建高效、灵活的API。首先通过引入`spring-boot-starter-data-graphql`等依赖支持GraphQL,然后定义查询和类型,利用`@GraphQLQuery`等注解实现具体功能。Spring的依赖注入和事务管理进一步增强了GraphQL服务的能力。示例展示了从查询到突变的具体实现,证明了Spring与GraphQL结合的强大潜力,适合现代API设计与开发。
|
Java测试技术网络架构
Spring REST实践之客户端和测试
RestTemplate 可参考spring实战来写这部分。 RestTemplate免于编写乏味的样板代码,RestTemplate定义了33个与REST资源交互的方法,涵盖了HTTP动作的各种形式,其实这些方法只有11个独立的方法,而每一个方法都由3个重载的变种。
125500
|
1月前
|
缓存JavaMaven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决

热门文章

最新文章