使用Spring Boot与Spring Security构建安全的RESTful API

引言

在现代Web应用开发中,安全性是一个不可忽视的重要方面。Spring Boot和Spring Security为开发者提供了一套强大的工具,用于构建安全的RESTful API。本文将详细介绍如何结合Spring Boot和Spring Security,并集成JWT(JSON Web Token)实现身份验证与授权。

技术栈

  • 核心框架: Spring Boot, Spring Security
  • 身份验证: JWT
  • 数据库: H2(示例用)
  • 构建工具: Maven

项目初始化

首先,我们需要创建一个Spring Boot项目。可以通过Spring Initializr(https://start.spring.io/)快速生成项目骨架。选择以下依赖:

  • Spring Web
  • Spring Security
  • H2 Database
  • Lombok(可选,简化代码)

配置Spring Security

Spring Security默认会为所有端点启用基本认证。我们需要自定义配置以实现JWT认证。

1. 添加JWT依赖

pom.xml中添加以下依赖:

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>

2. 创建JWT工具类

编写一个工具类用于生成和验证JWT:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;@Component
public class JwtTokenUtil {private String secret = "your-secret-key";public String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return createToken(claims, userDetails.getUsername());}private String createToken(Map<String, Object> claims, String subject) {return Jwts.builder().setClaims(claims).setSubject(subject).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, UserDetails userDetails) {final String username = extractUsername(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}private Boolean isTokenExpired(String token) {return extractExpiration(token).before(new Date());}public String extractUsername(String token) {return extractClaim(token, Claims::getSubject);}public Date extractExpiration(String token) {return extractClaim(token, Claims::getExpiration);}private <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();}
}

3. 配置Security Filter

创建一个过滤器,用于拦截请求并验证JWT:

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.stereotype.Component;
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;@Component
public class JwtRequestFilter extends OncePerRequestFilter {private final JwtTokenUtil jwtTokenUtil;private final UserDetailsServiceImpl userDetailsService;public JwtRequestFilter(JwtTokenUtil jwtTokenUtil, UserDetailsServiceImpl userDetailsService) {this.jwtTokenUtil = jwtTokenUtil;this.userDetailsService = userDetailsService;}@Overrideprotected 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 = jwtTokenUtil.extractUsername(jwt);}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}

4. 配置Spring Security

SecurityConfig类中配置Spring Security:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final UserDetailsServiceImpl userDetailsService;private final JwtRequestFilter jwtRequestFilter;public SecurityConfig(UserDetailsServiceImpl userDetailsService, JwtRequestFilter jwtRequestFilter) {this.userDetailsService = userDetailsService;this.jwtRequestFilter = jwtRequestFilter;}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected 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);}
}

实现认证与授权

1. 创建用户服务

实现UserDetailsService接口,用于加载用户信息:

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.ArrayList;@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 实际项目中应从数据库加载用户信息return new User("admin", "$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6", new ArrayList<>());}
}

2. 创建认证接口

创建一个控制器用于生成JWT:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AuthenticationController {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@PostMapping("/authenticate")public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) {authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(),authenticationRequest.getPassword()));final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());final String jwt = jwtTokenUtil.generateToken(userDetails);return ResponseEntity.ok(new AuthenticationResponse(jwt));}
}

测试API

使用Postman或curl测试以下端点:

  1. 获取JWT

    POST /authenticate
    {"username": "admin","password": "password"
    }
    
  2. 访问受保护资源

    GET /api/protected
    Headers: Authorization: Bearer <your-jwt-token>
    

总结

本文详细介绍了如何使用Spring Boot和Spring Security构建安全的RESTful API,并集成JWT实现身份验证与授权。通过实际代码示例,开发者可以快速掌握相关技术。

扩展阅读

  • Spring Security官方文档
  • JWT官方文档
  • Spring Boot官方文档

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人拖动示教控制

机器人拖动示教控制 机器人拖动视角控制与轨迹记录 1. 知识目标 体验ES机器人拖动视角操作体验ES机器人拖动轨迹记录 2. 技能目标 掌握ES机器人拖动视角操作掌握ES机器人拖动轨迹记录 3. ES机器人拖动视角操作 3.1 操作步骤 点击“拖动视角”按钮长按“启用”键约3秒进入…

RuoYi-Vue3-FastAPI框架的功能实现(上)

RuoYi-Vue3-FastAPI框架的功能实现&#xff08;上&#xff09; 感谢大佬给出关于python后端的若依框架&#xff0c;希望这个简单文档能帮助到大家。 安装与运行&#xff1a; 下载地址&#xff1a;Vue2版本&#xff1a; Gitte仓库地址&#xff1a;RuoYi-Vue-FastAPI: 基于Vu…

Paimon和Hive相集成

Paimon版本1.17 Hive版本3.1.3 1、Paimon集成Hive 将paimon-hive-connector.jar复制到auxlib中&#xff0c;下载链接Index of /groups/snapshots/org/apache/https://repository.apache.org/snapshots/org/apache/paimon/ 通过flink进入查看paimon /opt/softwares/flink-1…

【Leetcode 每日一题】3362. 零数组变换 III

问题背景 给你一个长度为 n n n 的整数数组 n u m s nums nums 和一个二维数组 q u e r i e s queries queries&#xff0c;其中 q u e r i e s [ i ] [ l i , r i ] queries[i] [l_i, r_i] queries[i][li​,ri​]。 每一个 q u e r i e s [ i ] queries[i] queries[i]…

计算机视觉与深度学习 | 用于图像分割的自监督学习(Self-Supervised Learning)方法综述

图像分割 用于图像分割的自监督学习(Self-Supervised Learning)方法综述**1. 背景与意义****2. 方法演进****3. 图像分割子任务与SSL策略****4. 自监督预训练任务分类****5. 基准数据集与评估指标****6. 挑战与未来方向****总结**用于图像分割的自监督学习(Self-Supervised …

Jenkins集成Docker与K8S构建

Jenkins 是一个开源的持续集成和持续交付(CI/CD)工具,广泛用于自动化软件开发过程中的构建、测试和部署任务。它通过插件系统提供了高度的可扩展性,支持与多种开发工具和技术的集成。 Jenkins 的核心功能 Jenkins 的主要功能包括自动化构建、测试和部署。它能够监控版本控…

使用 adb 命令截取 Android 设备的屏幕截图

使用 adb 命令截取 Android 设备的屏幕截图。以下是两种常见的方法&#xff1a; 方法一&#xff1a;截屏后保存到电脑 adb shell screencap -p /sdcard/screenshot.png adb pull /sdcard/screenshot.png解释&#xff1a; adb shell screencap -p /sdcard/screenshot.png&…

参与开发的注意事项

1.开发期间&#xff0c;不要擅自修改架构的内容 使用技术官发的项目文件夹来开发&#xff0c;而不是自己建立项目&#xff0c; 否则会导致环境不统一 架构内容&#xff1a;&#xff08;不能更改&#xff09; 1.类型定义&#xff0c;全局变量声明 2.函数申明&#xff08;函数名称…

linux安装nginx和前端部署vue项目

1、打包前端项目 npm run build 执行完后会在根目录下生成一个dist文件夹&#xff0c;这个dist文件夹就是我们后面要部署到nginx的东西。 2、将dist文件夹上传到服务器中 自己建一个目录&#xff0c;上传即可&#xff08;尽量不要在root目录下&#xff0c;可能涉及权限问题…

亲测有效!OGG 创建抽取进程报错 OGG-08241,如何解决?

前言 今天在测试 OGG 一个功能的时候&#xff0c;需要重新初始化 oggca&#xff0c;所以重装了一下 OGG。重建完之后重新添加抽取进程报错&#xff0c;一直无法添加成功&#xff1a; 经过一翻分析&#xff0c;找到了解决方案&#xff0c;本文记录一下解决过程。 问题描述 OG…

Docker构建 Dify 应用定时任务助手

概述 Dify 定时任务管理工具是一个基于 GitHub Actions 的自动化解决方案&#xff0c;用于实现 Dify Workflow 的定时执行和状态监控。无需再为缺乏定时任务支持而感到困扰&#xff0c;本工具可以帮助设置自动执行任务并获取实时通知&#xff0c;优化你的工作效率。 注意&…

ubuntu24.04+RTX5090D 显卡驱动安装

初步准备 Ubuntu默认内核太旧&#xff0c;用mainline工具安装新版&#xff1a; sudo add-apt-repository ppa:cappelikan/ppa sudo apt update && sudo apt full-upgrade sudo apt install -y mainline mainline list # 查看可用内核列表 mainline install 6.13 # 安装…

网络爬虫(Web Crawler)详解

网络爬虫(Web Crawler)详解 1. 基本概念与核心目标 定义: 网络爬虫是一种自动化的程序,通过HTTP协议访问网页,提取并存储数据(如文本、链接、图片),并根据策略递归访问新链接。核心目标: 数据采集:抓取特定网站或全网公开数据。索引构建:为搜索引擎提供页面内容(如…

大模型如何助力数学可视化?

大家好&#xff0c;我是 i 学习的老章 在数学学习和教学中&#xff0c;将抽象概念可视化对于理解至关重要。Manim 是一个强大的数学动画引擎&#xff0c;由著名数学科普视频作者 3Blue1Brown 开发并广为人知。 老章较早之前就介绍过 manim&#xff1a;B 站上爆红的数学视频&a…

Oracle基础知识(二)

目录 1.聚合函数 2.COUNT(1)&COUNT(*)&COUNT(字段)区别&#xff08;面试常问&#xff09; 3.分组聚合——group by 4.去重&#xff1a;DISTINCT 、GROUP BY 5.聚合函数的过滤HAVING 6.oracle中having与where的区别 (面试常问) 7.ROUND与TRUNC函数 8.ROLLUP上卷…

DTAS 3D多约束装配助力悬架公差分析尺寸链计算:麦弗逊/双叉臂/多连杆/H臂一网打尽

摘要&#xff1a;汽车四轮定位参数与悬架密切相关。汽车悬架对于车辆的行驶性能、安全性和舒适性至关重要。DTAS 3D提供了各类型悬架的公差仿真分析方法。 关键字&#xff1a;DTAS 3D、前后悬架、公差仿真分析、 运动耦合 一、悬架公差分析综述 悬架是车身&#xff08;或车架…

Serverless爬虫架构揭秘:动态IP、冷启动与成本优化

一、问题背景&#xff1a;旧技术的瓶颈 在传统爬虫架构中&#xff0c;我们通常部署任务在本地机器或虚拟机中&#xff0c;搭配定时器调度任务。虽然这种方式简单&#xff0c;但存在以下明显缺陷&#xff1a; 固定IP易被封禁&#xff1a;目标网站如拼多多会通过IP频率监控限制…

设备预测性维护的停机时间革命:中讯烛龙如何用AI重构工业设备管理范式

在工业4.0的智能化浪潮中&#xff0c;非计划停机每年吞噬企业3%-8%的产值。中讯烛龙预测性维护系统通过多模态感知矩阵分布式智能体的创新架构&#xff0c;实现设备健康管理的范式跃迁&#xff0c;帮助制造企业将停机时间压缩70%以上。本文将深度解析技术实现路径与行业级实践方…

Java面试攻略:从Spring Boot到微服务架构的深入探讨

Java面试攻略&#xff1a;从Spring Boot到微服务架构的深入探讨 场景设定 在一家知名互联网大厂的会议室里&#xff0c;资深面试官王老师正在对一位求职者谢飞机进行技术面试。谢飞机是一位幽默风趣的程序员&#xff0c;他的回答有时让人捧腹大笑。 第一轮&#xff1a;核心技…

LlamaIndex

1、大语言模型开发框架的价值是什么? SDK:Software Development Kit,它是一组软件工具和资源的集合,旨在帮助开发者创建、测试、部署和维护应用程序或软件。 所有开发框架(SDK)的核心价值,都是降低开发、维护成本。 大语言模型开发框架的价值,是让开发者可以更方便地…