使用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
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84047.shtml
英文地址,請注明出處:http://en.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;從而提升小程序的啟…