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

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

9526b1d6ced59dd11678343f8411959.png

7.1 保护 REST API

在数字化时代,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;
@Component
public 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.SessionCreation
Policy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
public 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;
@Component
public 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;
@EnableWebSecurity
public 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

目录
相关文章
|
30天前
|
Cloud Native API
微服务引擎 MSE 及云原生 API 网关 2024 年 9 月产品动态
微服务引擎 MSE 及云原生 API 网关 2024 年 9 月产品动态。
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
100 3
|
3天前
|
负载均衡 监控 API
dotnet微服务之API网关Ocelot
Ocelot 是一个基于 .NET 的 API 网关,适用于微服务架构。本文介绍了如何创建一个 Web API 项目并使用 Ocelot 进行 API 请求路由、负载均衡等。通过配置 `ocelot.json` 和修改 `Program.cs`,实现对 `GoodApi` 和 `OrderApi` 两个项目的路由管理。最终,通过访问 `https://localhost:7122/good/Hello` 和 `https://localhost:7122/order/Hello` 验证配置成功。
14 1
dotnet微服务之API网关Ocelot
|
6天前
|
运维 Cloud Native 应用服务中间件
阿里云微服务引擎 MSE 及 云原生 API 网关 2024 年 10 月产品动态
阿里云微服务引擎 MSE 面向业界主流开源微服务项目, 提供注册配置中心和分布式协调(原生支持 Nacos/ZooKeeper/Eureka )、云原生网关(原生支持Higress/Nginx/Envoy,遵循Ingress标准)、微服务治理(原生支持 Spring Cloud/Dubbo/Sentinel,遵循 OpenSergo 服务治理规范)能力。API 网关 (API Gateway),提供 APl 托管服务,覆盖设计、开发、测试、发布、售卖、运维监测、安全管控、下线等 API 生命周期阶段。帮助您快速构建以 API 为核心的系统架构.满足新技术引入、系统集成、业务中台等诸多场景需要
|
12天前
|
监控 安全 应用服务中间件
微服务架构下的API网关设计策略与实践####
本文深入探讨了在微服务架构下,API网关作为系统统一入口点的设计策略、实现细节及其在实际应用中的最佳实践。不同于传统的摘要概述,本部分将直接以一段精简的代码示例作为引子,展示一个基于NGINX的简单API网关配置片段,随后引出文章的核心内容,旨在通过具体实例激发读者兴趣,快速理解API网关在微服务架构中的关键作用及实现方式。 ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_service:5000;
|
16天前
|
监控 测试技术 API
确保微服务的API版本控制策略能够适应不断变化的业务需求
确保微服务的API版本控制策略能够适应不断变化的业务需求
|
17天前
|
缓存 API 网络架构
掌握现代API开发:GraphQL vs REST
【10月更文挑战第24天】本文深入探讨了现代API开发中两种主流技术——GraphQL和REST的设计理念、技术特点及实际开发中的对比分析。GraphQL通过声明式数据请求和强类型系统提供更高的灵活性和性能,而REST则以其无状态特性和成熟的生态系统见长。文章还讨论了两者在客户端-服务器交互、安全性和工具支持方面的优劣,帮助开发者根据项目需求做出明智选择。
|
21天前
|
监控 测试技术 API
如何确保微服务的API版本控制策略能够适应不断变化的业务需求?
如何确保微服务的API版本控制策略能够适应不断变化的业务需求?
|
27天前
|
监控 负载均衡 API
Web、RESTful API 在微服务中有哪些作用?
在微服务架构中,Web 和 RESTful API 扮演着至关重要的角色。它们帮助实现服务之间的通信、数据交换和系统的可扩展性。
47 2
|
1月前
|
运维 Cloud Native 应用服务中间件
阿里云微服务引擎 MSE 及 云原生 API 网关 2024 年 09 月产品动态
阿里云微服务引擎 MSE 面向业界主流开源微服务项目, 提供注册配置中心和分布式协调(原生支持 Nacos/ZooKeeper/Eureka )、云原生网关(原生支持Higress/Nginx/Envoy,遵循Ingress标准)、微服务治理(原生支持 Spring Cloud/Dubbo/Sentinel,遵循 OpenSergo 服务治理规范)能力。API 网关 (API Gateway),提供 APl 托管服务,覆盖设计、开发、测试、发布、售卖、运维监测、安全管控、下线等 API 生命周期阶段。帮助您快速构建以 API 为核心的系统架构.满足新技术引入、系统集成、业务中台等诸多场景需要

热门文章

最新文章