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

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

引言

在现代Web应用开发中,安全性是至关重要的。Spring Boot和Spring Security是Java生态中广泛使用的框架,它们提供了强大的工具来保护RESTful API。本文将介绍如何结合Spring Boot和Spring Security,并使用JWT(JSON Web Token)实现身份验证与授权。

技术栈

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

项目初始化

首先,使用Spring Initializr创建一个新的Spring Boot项目,添加以下依赖:

  • Spring Web
  • Spring Security
  • H2 Database
  • Lombok(可选)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

配置Spring Security

Spring Security默认会为所有端点启用基本身份验证。我们需要自定义配置以支持JWT。

1. 创建JWT工具类

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;@Component
public class JwtTokenUtil {private final String SECRET_KEY = "your-secret-key";public String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时有效期.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public Boolean validateToken(String token, UserDetails userDetails) {final String username = extractUsername(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}private String extractUsername(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();}private Boolean isTokenExpired(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration().before(new Date());}
}

2. 自定义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 CustomUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 实际项目中应从数据库加载用户信息return new User("admin", "password", new ArrayList<>());}
}

3. 配置SecurityFilterChain

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.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/authenticate").permitAll().anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);http.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);return http.build();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}@Beanpublic JwtRequestFilter jwtRequestFilter() {return new JwtRequestFilter();}
}

实现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 {@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.customUserDetailsService.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);}
}

测试API

1. 创建认证端点

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 CustomUserDetailsService 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));}
}

2. 测试受保护端点

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ProtectedController {@GetMapping("/protected")public String protectedEndpoint() {return "This is a protected endpoint!";}
}

总结

本文详细介绍了如何使用Spring Boot和Spring Security结合JWT实现安全的RESTful API。通过自定义配置和过滤器,我们能够轻松实现身份验证与授权功能。希望本文能帮助开发者快速上手现代Web应用的安全实践。

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

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

相关文章

虚幻引擎5-Unreal Engine笔记之`GameMode`、`关卡(Level)` 和 `关卡蓝图(Level Blueprint)`的关系

虚幻引擎5-Unreal Engine笔记之GameMode、关卡&#xff08;Level&#xff09; 和 关卡蓝图&#xff08;Level Blueprint&#xff09;的关系 code review! 参考笔记&#xff1a; 1.虚幻引擎5-Unreal Engine笔记之GameMode、关卡&#xff08;Level&#xff09; 和 关卡蓝图&…

Java+Selenium+快代理实现高效爬虫

目录 一、前言二、Selenium简介三、环境准备四、代码实现4.1 创建WebDriver工厂类4.2 创建爬虫主类4.3 配置代理的注意事项 六、总结与展望 一、前言 在Web爬虫技术中&#xff0c;Selenium作为一款强大的浏览器自动化工具&#xff0c;能够模拟真实用户操作&#xff0c;有效应对…

SpringBoot配置文件的合并

需求:想分类将mysql数据库的配置放在一个文件,redis的配置放在另外一个文件 就不去引入mysql和redis了,看能否得到值就行了 测试结果 model的包放错了 应该移动到demo里 能否用yml或者yaml呢 这里注意yml的写法 测试结果也是可以的 注意如果主配置文件是yml或者yaml的话

深入理解 BFC:网页布局的关键机制

在前端开发的世界里&#xff0c;网页布局是一项至关重要的任务。而在众多布局相关的概念中&#xff0c;BFC&#xff08;Block Formatting Context&#xff0c;块级格式化上下文&#xff09;扮演着极为关键的角色。今天&#xff0c;就让我们深入剖析 BFC 的方方面面。 一、BFC …

04-Web后端基础(基础知识)

而像HTML、CSS、JS 以及图片、音频、视频等这些资源&#xff0c;我们都称为静态资源。 所谓静态资源&#xff0c;就是指在服务器上存储的不会改变的数据&#xff0c;通常不会根据用户的请求而变化。 那与静态资源对应的还有一类资源&#xff0c;就是动态资源。那所谓动态资源&…

Vue3 Element Plus el-table-column Sortable 排序失效

问题描述&#xff1a; vue3中 element plus 中 el-table 的 el-table-column使用了插槽后&#xff0c;为什么sortable不起效果&#xff0c;不能点击排序 <el-table-columnlabel"记账日期"width"110"fixed"left"header-align"left"…

Unity中SRP Batcher使用整理

SRP Batcher 是一种绘制调用优化,可显著提高使用 SRP 的应用程序的性能,SRP Batcher 减少了Unity为使用相同着色器变体的材质准备和调度绘制调用所需的CPU 时间。 工作原理: 传统优化方法通过减少绘制调用次数提升性能,而SRP Batcher的核心理念在于降低绘制调用间的渲染状…

服务器的基础知识

什么是服务器 配置牛、运行稳、价格感人的高级计算机&#xff0c;家用电脑不能比拟的。 服务器的组成&#xff1a;电源、raid卡、网卡、内存、cpu、主板、风扇、硬盘。 服务器的分类 按计算能力分类 超级计算机 小型机AIX x86服务器&#xff08;服务器cpu架构&#xff09; …

服务器网络配置 netplan一个网口配置两个ip(双ip、辅助ip、别名IP别名)

文章目录 问答 问 # This is the network config written by subiquity network:ethernets:enp125s0f0:dhcp4: noaddresses: [192.168.90.180/24]gateway4: 192.168.90.1nameservers:addresses:- 172.0.0.207- 172.0.0.208enp125s0f1:dhcp4: trueenp125s0f2:dhcp4: trueenp125…

高级SQL技巧:时序数据查询优化与性能调优实战

高级SQL技巧&#xff1a;时序数据查询优化与性能调优实战 引言 在现代数据驱动型系统中&#xff0c;时序数据&#xff08;时间序列数据&#xff09;正成为企业核心资产之一。然而&#xff0c;随着数据量激增和复杂业务需求的不断涌现&#xff0c;传统的SQL查询方式已难以满足…

DDoS攻击应对指南:提升网站安全性的有效策略

DDoS&#xff08;分布式拒绝服务&#xff09;攻击成为了企业面临的主要网络安全威胁之一。随着技术的不断发展&#xff0c;DDoS攻击手段也在不断升级&#xff0c;给企业的网络安全带来了极大的挑战。针对这一问题&#xff0c;企业需要采取有效的防御措施&#xff0c;以保障网站…

Appium 的 enableMultiWindows 参数

引言 在移动应用自动化测试中&#xff0c;​​混合应用&#xff08;Hybrid App&#xff09;​​ 和多窗口场景&#xff08;如分屏、弹窗、多 WebView&#xff09;的处理一直是技术难点。Appium 的 enableMultiWindows 参数为这类场景提供了关键支持&#xff0c;但在实际使用中常…

C++中的菱形继承问题

假设有一个问题&#xff0c;类似于鸭子这样的动物有很多种&#xff0c;如企鹅和鱿鱼&#xff0c;它们也可能会有一些共同的特性。例如&#xff0c;我们可以有一个叫做 AquaticBird &#xff08;涉禽&#xff0c;水鸟的一类&#xff09;的类&#xff0c;它又继承自 Animal 和 Sw…

前端excel表格解析为json,并模仿excel显示

前端环境&#xff1a;elementUI vue2 <style lang"scss" scoped> 页面效果 jsondata为mock数据&#xff0c;为方便调试其内容可清空&#xff0c;首行&#xff08;字母坐标&#xff09;随数据内容自动变化&#xff0c;首列也是一样&#xff0c;模拟excel …

NAT(网络地址转换)逻辑图解+实验详解

原理 NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09; 是一种网络技术&#xff0c;用于在IP数据包通过路由器或防火墙时&#xff0c;修改其源IP地址或目标IP地址&#xff0c;以实现不同网络之间的通信。 基础概念 本来NAT是来解决 IPv4 地…

Qt+线段拖曳示例代码

Qt线段拖曳示例代码&#xff0c;功能见下图。 代码如下&#xff1a; canvaswidget.h #ifndef CANVASWIDGET_H #define CANVASWIDGET_H#include <QWidget> #include <QPainter> #include <QMouseEvent> #include <QVector>class CanvasWidget : publi…

高等数学-求导

一、求导数的原函数就是求导数的积分 1&#xff09;设函数f(t)在区间[a,b]上连续&#xff0c;则对任意的x∈[a,b],f(t)在[a,x]上连续&#xff0c;从而在[a,x]上可积。令其积分为Φ(x)∫*a^x f(t)dt, x∈[a,b],则Φ(x)为定义在区间[a,b]上的一个函数&#xff0c;通常称作积分上…

(第94天)OGG 微服务搭建 Oracle 19C CDB 架构同步

前言 Oracle GoldenGate Microservice Architecture (OGGMA) 是在 OGG 12.3 版本推出的全新架构。相比传统架构,OGGMA 基于 Rest API,通过 WEB 界面即可完成 OGG 的配置和监控,大大简化了部署和管理流程。 本文将详细介绍如何在 Oracle 19C CDB 环境中部署 OGG 19.1.0.4 微…

前端vscode学习

1.安装python 打开Python官网&#xff1a;Welcome to Python.org 一定要点PATH&#xff0c;要不然要自己设 点击install now,就自动安装了 键盘winR 输入cmd 点击确定 输入python&#xff0c;回车 显示这样就是安装成功了 2.安装vscode 2.1下载软件 2.2安装中文 2.2.1当安…

uniapp vue 开发微信小程序 分包梳理经验总结

嗨&#xff0c;我是小路。今天主要和大家分享的主题是“uniapp vue 开发微信小程序 分包梳理经验总结”。 在使用 UniAppvue框架开发微信小程序时&#xff0c;当项目比较大的时候&#xff0c;经常需要分包加载。它有助于控制主包的大小&#xff0c;从而提升小程序的启…