欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?Security的應(yīng)用案例講解

 更新時(shí)間:2025年09月17日 11:43:44   作者:熙客  
Spring?Security支持多認(rèn)證方式(如OAuth2、JWT),實(shí)現(xiàn)基于角色的授權(quán)與防護(hù)(CSRF、會話管理),無縫集成Spring生態(tài),適用于構(gòu)建微服務(wù)安全體系,本文給大家介紹Spring?Security的基本使用示例,感興趣的朋友跟隨小編一起看看吧

Spring Security原理

Spring Security是一個強(qiáng)大的身份驗(yàn)證和訪問控制框架,它提供了一套全面的安全解決方案,包括身份驗(yàn)證、授權(quán)、防止攻擊等功能,用于保護(hù)Spring應(yīng)用程序。它的設(shè)計(jì)理念是基于過濾器鏈(Filter Chain)和委托模式,通過一系列的過濾器來處理不同的安全功能。 

1. 過濾器鏈(Filter Chain): Spring Security通過過濾器鏈的方式來處理安全性。每個過濾器負(fù)責(zé)一個特定的安全功能,例如身份驗(yàn)證、授權(quán)、會話管理等。過濾器鏈?zhǔn)怯行虻模埱髸来瓮ㄟ^這些過濾器,每個過濾器都有機(jī)會對請求進(jìn)行處理。

2. 委托模式: Spring Security使用委托模式將不同的安全功能委托給不同的組件。例如,身份驗(yàn)證(Authentication)的實(shí)現(xiàn)被委托給AuthenticationManager,而授權(quán)(Authorization)的實(shí)現(xiàn)則被委托給AccessDecisionManager。

3. 安全上下文(SecurityContext): 安全上下文是一個存儲當(dāng)前用戶的地方,可以通過SecurityContextHolder來訪問。它包含了當(dāng)前用戶的身份驗(yàn)證信息(Authentication)以及其他與安全相關(guān)的信息。

官方文檔

一、核心能力

1.1身份認(rèn)證 (Authentication) - “你是誰?”

  • 多種認(rèn)證方式:支持幾乎所有主流認(rèn)證方案,如表單登錄(Username/Password)、HTTP Basic、HTTP Digest、OAuth 2.0、OIDC (OpenID Connect)、SAML 2.0、LDAP、JAAS、Pre-Authentication(如CAS)等。
    • 表單登錄:最常用的方式,提供默認(rèn)的登錄頁。
    • HTTP Basic 認(rèn)證:常用于 REST API。
    • OAuth 2.0 / OpenID Connect:支持第三方登錄(如使用 Google, GitHub, Facebook 登錄)。
    • LDAP:支持企業(yè)級目錄服務(wù)認(rèn)證。
    • JAAS:Java 認(rèn)證和授權(quán)服務(wù)。
    • 自定義認(rèn)證:你可以集成任何你想用的認(rèn)證方式。
  • 靈活的密碼編碼:內(nèi)置支持多種密碼加密器(如BCrypt、SCrypt、Pbkdf2、Argon2),并強(qiáng)烈推薦使用BCrypt,防止密碼明文存儲。
  • “記住我”功能:通過持久化或基于令牌的機(jī)制實(shí)現(xiàn)長期登錄(通過 cookie 實(shí)現(xiàn)長期會話)。
  • 多因素認(rèn)證 (MFA):可以集成TOTP(如Google Authenticator)等二次驗(yàn)證手段。
  • 與現(xiàn)有系統(tǒng)集成:可以輕松地與已有的數(shù)據(jù)庫表結(jié)構(gòu)、用戶服務(wù)進(jìn)行對接。

1.2授權(quán) (Authorization) - “你能做什么?”

  • 請求級別授權(quán):基于URL模式,控制用戶對某個API或頁面的訪問權(quán)限(例如 /admin/** 需要 ROLE_ADMIN 角色)。
  • 方法級別授權(quán):通過注解(如 @PreAuthorize@PostAuthorize@Secured)在Service層或Controller層的方法上進(jìn)行精細(xì)化的權(quán)限控制。
  • 訪問控制列表 (ACL):支持對領(lǐng)域?qū)ο螅―omain Object) 進(jìn)行非常細(xì)粒度的權(quán)限控制(例如,用戶A可以“讀”文檔1,但不能“刪除”它)。這是一個相對復(fù)雜的功能,適用于特定場景。
  • 動態(tài)權(quán)限:權(quán)限規(guī)則可以從數(shù)據(jù)庫或其他動態(tài)源加載,實(shí)現(xiàn)高度靈活的權(quán)限管理。

1.3防護(hù)常見攻擊

  • CSRF (跨站請求偽造):默認(rèn)開啟防護(hù),尤其對非冪等的POST、PUT等請求進(jìn)行令牌驗(yàn)證。
  • Session Fixation (會話固定):默認(rèn)防護(hù),認(rèn)證成功后會自動創(chuàng)建新的Session。防止 Session 固定攻擊、控制并發(fā)會話數(shù)(單一用戶最多同時(shí)在線數(shù))、Session 超時(shí)處理等。
  • 點(diǎn)擊劫持:可以通過設(shè)置HTTP頭 X-Frame-Options 來防護(hù)。
  • CORS (跨域資源共享):提供便捷的配置方式。
  • 安全頭:自動設(shè)置一系列安全相關(guān)的HTTP頭,如 Content-Security-PolicyX-Content-Type-OptionsX-Frame-OptionsStrict-Transport-Security 等來增強(qiáng)瀏覽器端的安全性。

1.4與其他技術(shù)無縫集成

  • Spring生態(tài)系統(tǒng):與Spring Boot、Spring MVC、Spring WebFlux、Spring Data 深度整合,開箱即用,配置簡便。Spring Boot 更是通過自動配置讓集成變得極其簡單。
  • Servlet API:基于Servlet Filter實(shí)現(xiàn),適用于任何Servlet容器(Tomcat, Jetty等)。
  • 微服務(wù)架構(gòu):是構(gòu)建微服務(wù)安全(如資源服務(wù)器、OAuth2客戶端)的事實(shí)標(biāo)準(zhǔn)。

1.5 能力邊界

  • ? 在其邊界內(nèi)(做得好)
  • 應(yīng)用級別的身份認(rèn)證和授權(quán)。
  • 會話管理。
  • 防護(hù)基于Web的常見攻擊(CSRF, XSS的頭防護(hù)等)。
  • ? 超出其邊界(不擅長或不做)
    • 網(wǎng)絡(luò)層安全:如防火墻規(guī)則、VPN、DDoS防護(hù)、SSL/TLS終止(通常由網(wǎng)關(guān)/負(fù)載均衡器負(fù)責(zé))。
    • 操作系統(tǒng)/容器安全:如Linux內(nèi)核安全加固、Docker鏡像漏洞掃描。
    • 數(shù)據(jù)安全:如數(shù)據(jù)庫加密、數(shù)據(jù)傳輸過程中的加密(應(yīng)由TLS負(fù)責(zé))。
    • 業(yè)務(wù)邏輯漏洞:無法自動防止業(yè)務(wù)層面的漏洞(例如,水平越權(quán):用戶A通過修改ID訪問了用戶B的數(shù)據(jù),需要在授權(quán)邏輯中手動編寫檢查代碼)。
    • 安全審計(jì)與日志:雖然可以與審計(jì)集成,但專業(yè)的日志分析和審計(jì)通常由ELK、Splunk等專用系統(tǒng)完成。
    • WAF (Web應(yīng)用防火墻) 功能:雖然能防護(hù)一些攻擊,但無法替代專業(yè)的WAF來防護(hù)復(fù)雜的SQL注入、XSS等攻擊(WAF基于規(guī)則和模式匹配,在更底層工作)。

二、核心架構(gòu)與原理

Spring Security 的核心設(shè)計(jì)理念非常清晰:在 Servlet 過濾器(Filter)層面,為每一個進(jìn)入應(yīng)用的 HTTP 請求提供一系列的身份認(rèn)證(Authentication)和授權(quán)(Authorization)檢查。

它本質(zhì)上是一個過濾器鏈,請求必須逐一通過這條鏈上的每個過濾器,才能最終訪問到你的 Controller 中的資源。如果任何一個過濾器檢查失敗,請求就會被重定向、拋出異?;蛑苯臃祷劐e誤信息。

2.1 HTTP完整的請求過程

  • 請求到達(dá): HTTP 請求進(jìn)入應(yīng)用。
  • 遍歷過濾器鏈: 請求依次經(jīng)過 Spring Security 的各個過濾器。
  • 建立安全上下文SecurityContextPersistenceFilter 從 Session 中恢復(fù)用戶的 SecurityContext(如果已登錄)或創(chuàng)建一個空的。
  • 處理登錄/認(rèn)證:
    • 如果是登錄請求(如 /login POST),UsernamePasswordAuthenticationFilter 會攔截它,提取用戶名密碼,發(fā)起認(rèn)證流程。
    • 認(rèn)證成功,一個包含用戶信息和權(quán)限的、已認(rèn)證的 Authentication 對象會被放入 SecurityContext,并通常保存到 Session 中。
  • 處理匿名用戶: 如果用戶未認(rèn)證,AnonymousAuthenticationFilter 會放入一個匿名 Token。
  • 異常轉(zhuǎn)換ExceptionTranslationFilter 準(zhǔn)備捕獲后續(xù)的異常。
  • 授權(quán)決策: 請求到達(dá)最終的 FilterSecurityInterceptor。
    • 它提取當(dāng)前請求對應(yīng)的權(quán)限規(guī)則 (ConfigAttribute)。
    • 它從 SecurityContextHolder 中獲取已認(rèn)證的 Authentication 對象。
    • 它調(diào)用 AccessDecisionManager 進(jìn)行投票決策。
  • 決策結(jié)果:
  • 允許訪問: 調(diào)用 FilterChain.doFilter(),請求最終到達(dá)你的 Controller,返回響應(yīng)。
  • 拒絕訪問: 拋出 AccessDeniedException
  • 異常處理ExceptionTranslationFilter 捕獲到異常:
    • 如果是 AuthenticationException (認(rèn)證失敗,用戶未知),啟動認(rèn)證流程:清除 SecurityContext,調(diào)用 AuthenticationEntryPoint(例如:重定向到登錄頁或返回 WWW-Authenticate 頭)。
    • 如果是 AccessDeniedException (授權(quán)失敗,權(quán)限不足),拒絕訪問:調(diào)用 AccessDeniedHandler(例如:返回 403 錯誤頁面)。
  • 清理上下文: 請求處理完畢,SecurityContextPersistenceFilter 將 SecurityContext 保存回 Session(如果需要),并清空 ThreadLocal。

2.2 核心組成

2.2.1 過濾器鏈 (Filter Chain) - 心臟

這是 Spring Security 最核心的概念。整個安全機(jī)制都構(gòu)建在 Servlet 規(guī)范定義的 Filter 之上。當(dāng)一個 HTTP 請求到來時(shí),它會經(jīng)過一個由多個安全過濾器組成的鏈條。

核心過濾器(按典型順序):

  • ChannelProcessingFilter: 決定是否需要重定向到 HTTPS 或 HTTP。
  • SecurityContextPersistenceFilter: 至關(guān)重要。在請求開始時(shí),從配置的 SecurityContextRepository(默認(rèn)是 HttpSessionSecurityContextRepository)中讀取 SecurityContext(安全上下文,包含用戶認(rèn)證信息),并將其設(shè)置到 SecurityContextHolder 中;在請求結(jié)束后,清空 SecurityContextHolder,并可能將 SecurityContext 保存回會話。
  • CorsFilter: 處理跨域請求 (CORS)。
  • CsrfFilter: 提供跨站請求偽造 (CSRF) 保護(hù)。
  • LogoutFilter: 匹配退出登錄的 URL(如 /logout),處理用戶退出邏輯,清除認(rèn)證信息。
  • UsernamePasswordAuthenticationFilter: 核心認(rèn)證過濾器。嘗試處理表單登錄請求。它從 POST 請求中提取用戶名和密碼,創(chuàng)建一個 UsernamePasswordAuthenticationToken(一個 Authentication 接口的實(shí)現(xiàn))并進(jìn)行認(rèn)證。
  • DefaultLoginPageGeneratingFilter: 如果沒有配置登錄頁面,這個過濾器會生成一個默認(rèn)的登錄頁。
  • DefaultLogoutPageGeneratingFilter: 生成默認(rèn)的退出頁面。
  • BasicAuthenticationFilter: 處理 HTTP Basic 認(rèn)證頭。
  • RequestCacheAwareFilter: 用于在用戶認(rèn)證成功后,恢復(fù)因登錄而中斷的原始請求。
  • SecurityContextHolderAwareRequestFilter: 包裝原始的 HttpServletRequest,提供一些 Spring Security 特有的方法,如 getRemoteUser()isUserInRole() 等。
  • AnonymousAuthenticationFilter: 至關(guān)重要。如果此時(shí) SecurityContextHolder 中還沒有認(rèn)證信息(即用戶未登錄),它會創(chuàng)建一個匿名的 Authentication 對象(AnonymousAuthenticationToken)并放入其中。這確保了安全上下文中永遠(yuǎn)有一個 Authentication 對象,避免了空指針異常,統(tǒng)一了“已認(rèn)證”和“未認(rèn)證”的處理邏輯。
  • SessionManagementFilter: 處理會話相關(guān)的策略,如同一個用戶的會話數(shù)量控制(防止同一賬號多次登錄)。
  • ExceptionTranslationFilter: 至關(guān)重要。它是整個過濾器鏈的“看門人”,負(fù)責(zé)捕獲后續(xù)過濾器(特別是 FilterSecurityInterceptor)拋出的異常,并將其轉(zhuǎn)換為相應(yīng)的行為(如重定向到登錄頁、返回 403 錯誤等)。它本身不進(jìn)行認(rèn)證或授權(quán)。
  • FilterSecurityInterceptor: 最終大門。這是授權(quán)發(fā)生的地方。它從 SecurityContextHolder 中獲取已認(rèn)證的 Authentication 對象,然后根據(jù)配置的權(quán)限規(guī)則(訪問屬性配置,如 hasRole(‘ADMIN’)),決定是允許請求繼續(xù)(調(diào)用 FilterChain.doFilter())還是拒絕訪問(拋出 AccessDeniedException)。

工作流程簡化視圖:
HTTP Request -> Filter1 -> Filter2 -> ... -> FilterSecurityInterceptor -> DispatcherServlet -> Your Controller

2.2.2 認(rèn)證 (Authentication) 核心組件

  • Authentication 接口: 代表一個認(rèn)證請求或一個已認(rèn)證的主體(用戶)。它包含:
  • principal: 主體標(biāo)識,通常是用戶名、UserDetails 對象或用戶ID。
  • credentials: 憑證,通常是密碼。認(rèn)證成功后通常會擦除。
  • authorities: 權(quán)限集合,即 GrantedAuthority 對象列表。
  • SecurityContext 接口: 持有 Authentication 對象。SecurityContextHolder.getContext().getAuthentication() 是獲取當(dāng)前用戶信息的標(biāo)準(zhǔn)方式。
  • SecurityContextHolder: 存儲 SecurityContext 的策略容器。默認(rèn)使用 ThreadLocal 策略,這意味著每個線程都有自己的 SecurityContext,從而保證了用戶請求之間的隔離。
  • AuthenticationManager: 認(rèn)證的入口/大門。它只有一個方法:authenticate(Authentication authentication)。你通常不會直接使用它。
  • ProviderManagerAuthenticationManager 最常用的實(shí)現(xiàn)。它本身不處理認(rèn)證,而是委托給一個 AuthenticationProvider 列表。它會遍歷這個列表,直到有一個 Provider 能夠處理當(dāng)前的 Authentication 類型。
  • AuthenticationProvider: 執(zhí)行具體認(rèn)證邏輯的組件。例如:
    • DaoAuthenticationProvider: 最常用的 Provider,從數(shù)據(jù)庫(DAO)中獲取用戶信息進(jìn)行認(rèn)證。它需要依賴一個 UserDetailsService。
    • JwtAuthenticationProvider: 用于處理 JWT Token 認(rèn)證。
    • LdapAuthenticationProvider: 用于 LDAP 認(rèn)證。
    • UserDetailsService: 核心接口,只有一個方法 loadUserByUsername(String username)。它負(fù)責(zé)從存儲系統(tǒng)(數(shù)據(jù)庫、內(nèi)存等)中根據(jù)用戶名加載用戶信息,并返回一個 UserDetails 對象。這是你需要自定義實(shí)現(xiàn)的最常見接口。
    • UserDetails: 接口,代表從系統(tǒng)存儲中加載出來的用戶信息,包括用戶名、密碼、權(quán)限、賬戶是否過期等??蚣芴峁┑膶?shí)現(xiàn)是 User。

認(rèn)證數(shù)據(jù)流:
UsernamePasswordAuthenticationFilter -> 創(chuàng)建 UsernamePasswordAuthenticationToken (未認(rèn)證) -> 調(diào)用 ProviderManager.authenticate() -> 委托給 DaoAuthenticationProvider -> 調(diào)用 UserDetailsService.loadUserByUsername() -> 獲取 UserDetails -> 比較密碼 -> 認(rèn)證成功 -> 返回一個已認(rèn)證的 Authentication 對象 -> 被過濾器設(shè)置到 SecurityContextHolder 中。

2.2.3 授權(quán) (Authorization) 核心組件

  • AccessDecisionManager: 授權(quán)的決策管理器。它通過輪詢一組 AccessDecisionVoter 并進(jìn)行投票,最終根據(jù)投票策略決定是否允許訪問。
  • AccessDecisionVoter: 投票器。它檢查當(dāng)前用戶的 Authentication 和受保護(hù)對象所需的配置屬性(ConfigAttribute,如 ROLE_ADMIN),然后投贊成、反對或棄權(quán)票。
  • ConfigAttribute: 保存著訪問受保護(hù)資源(如一個URL)所需的權(quán)限信息。通常來自你的配置:.antMatchers("/admin/**").hasRole("ADMIN") 中的 hasRole("ADMIN") 就是一個 ConfigAttribute。
  • FilterSecurityInterceptor: 如上所述,它是授權(quán)發(fā)生的觸發(fā)器。它調(diào)用 AccessDecisionManager 進(jìn)行決策。

授權(quán)數(shù)據(jù)流:
請求到達(dá) FilterSecurityInterceptor -> 獲取受保護(hù)資源的 ConfigAttribute -> 調(diào)用 AccessDecisionManager.decide() -> 輪詢所有 AccessDecisionVoter.vote() -> 根據(jù)投票策略(如“一票否決”、“多數(shù)同意”)做出最終決定 -> 允許訪問或拋出 AccessDeniedException -> 被上層的 ExceptionTranslationFilter 捕獲處理。

三、基本使用示例

需求:SpringBoot整合Spring Security頁面登陸,要求用戶信息存入數(shù)據(jù)庫,且密碼加密存儲,登錄成功后返回JWT令牌用于后續(xù)請求認(rèn)證;要求體現(xiàn)不同用戶授予不同權(quán)限;要求必要的安全配置。

安全特性:

  • 密碼使用BCrypt加密存儲
  • 基于角色的訪問控制
  • JWT令牌認(rèn)證,無狀態(tài)會話。完整的安全JWT流程
    • 登錄:用戶憑據(jù)驗(yàn)證 → 生成簽名JWT
    • 傳輸:通過HTTPS傳輸 → 防止竊聽
    • 存儲:客戶端安全存儲 → 防止XSS
    • 使用:每個請求攜帶 → 認(rèn)證用戶
    • 驗(yàn)證:服務(wù)器驗(yàn)證簽名和有效期 → 防止篡改
    • 注銷:客戶端刪除令牌 → 服務(wù)器可黑名單
  • CSRF保護(hù)禁用(因使用JWT)
  • 會話管理設(shè)置為無狀態(tài)

項(xiàng)目結(jié)構(gòu):

src/
├── main/
│   ├── java/com/example/demo/
│   │   ├── config/
│   │   │   ├── SecurityConfig.java
│   │   │   ├── JwtAuthenticationFilter.java
│   │   │   └── JwtUtil.java
│   │   ├── controller/
│   │   │   ├── AuthController.java
│   │   │   └── TestController.java
│   │   ├── entity/
│   │   │   ├── User.java
│   │   │   └── Role.java
│   │   ├── mapper/
│   │   │   └── UserMapper.java
│   │   ├── service/
│   │   │   ├── UserService.java
│   │   │   └── CustomUserDetailsService.java
│   │   └── DemoApplication.java
│   └── resources/
│       ├── application.properties
│       ├── schema.sql
│       └── mapper/UserMapper.xml

3.1 依賴配置 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0.0</version>
    <properties>
        <java.version>11</java.version>
    </properties>
    <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>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <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>
    </dependencies>
</project>

3.2 應(yīng)用配置 (application.properties)

# 服務(wù)器端口
server.port=8080
# 數(shù)據(jù)庫配置
spring.datasource.url=jdbc:mysql://localhost:3306/security_demo?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.demo.entity
# JWT密鑰
jwt.secret=mySecretKey
jwt.expiration=86400

密鑰配置:

  • 密鑰長度至少與哈希算法安全性要求一致(HS512建議至少64字節(jié))
  • 生產(chǎn)環(huán)境應(yīng)從安全配置源獲取密鑰(環(huán)境變量、密鑰管理服務(wù))
  • 定期輪換密鑰
# 使用足夠長且復(fù)雜的密鑰
jwt.secret=mySuperLongAndComplexSecretKeyThatIsHardToGuess123!

雖然代碼中不直接體現(xiàn),但部署時(shí)必須使用HTTPS,防止中間人攻擊,加密整個通信通道

# 生產(chǎn)環(huán)境應(yīng)強(qiáng)制使用HTTPS
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
server.ssl.key-store-type=PKCS12

3.3 數(shù)據(jù)庫初始化 (schema.sql)

CREATE DATABASE IF NOT EXISTS security_demo;
USE security_demo;
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    enabled BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE IF NOT EXISTS roles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS user_roles (
    user_id INT NOT NULL,
    role_id INT NOT NULL,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id)
);
-- 插入角色數(shù)據(jù)
INSERT IGNORE INTO roles (name) VALUES ('ROLE_USER');
INSERT IGNORE INTO roles (name) VALUES ('ROLE_ADMIN');
-- 插入用戶數(shù)據(jù)(密碼使用BCrypt加密,原始密碼均為"password")
INSERT IGNORE INTO users (username, password, enabled) VALUES 
('user', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 1),
('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 1);
-- 分配角色
INSERT IGNORE INTO user_roles (user_id, role_id) VALUES 
(1, 1), -- user has ROLE_USER
(2, 2); -- admin has ROLE_ADMIN

3.4 實(shí)體類

// User.java
package com.example.demo.entity;
import java.util.List;
public class User {
    private Long id;
    private String username;
    private String password;
    private Boolean enabled;
    private List<Role> roles;
    // 構(gòu)造方法、getter和setter
    public User() {}
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    // 省略getter和setter
}
// Role.java
package com.example.demo.entity;
public class Role {
    private Long id;
    private String name;
    // 構(gòu)造方法、getter和setter
    public Role() {}
    public Role(String name) {
        this.name = name;
    }
    // 省略getter和setter
}

3.5 MyBatis Mapper接口和XML

// UserMapper.java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
    User findByUsername(String username);
    User findById(Long id);
}
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="userResultMap" type="User">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="password" column="password" />
        <result property="enabled" column="enabled" />
        <collection property="roles" ofType="Role">
            <id property="id" column="role_id" />
            <result property="name" column="role_name" />
        </collection>
    </resultMap>
    <select id="findByUsername" resultMap="userResultMap">
        SELECT u.*, r.id as role_id, r.name as role_name
        FROM users u
        LEFT JOIN user_roles ur ON u.id = ur.user_id
        LEFT JOIN roles r ON ur.role_id = r.id
        WHERE u.username = #{username}
    </select>
    <select id="findById" resultMap="userResultMap">
        SELECT u.*, r.id as role_id, r.name as role_name
        FROM users u
        LEFT JOIN user_roles ur ON u.id = ur.user_id
        LEFT JOIN roles r ON ur.role_id = r.id
        WHERE u.id = #{id}
    </select>
</mapper>

3.6 服務(wù)層

// CustomUserDetailsService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.List;
import java.util.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用戶不存在: " + username);
        }
        List<GrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(
                user.getUsername(), 
                user.getPassword(), 
                authorities);
    }
}
// UserService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }
}

3.7 JWT工具類

package com.example.demo.config;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
 * JWT工具類 - 負(fù)責(zé)JWT令牌的生成、解析和驗(yàn)證
 * 
 * 安全特性說明:
 * 1. 使用HMAC-SHA512算法進(jìn)行簽名,確保令牌完整性
 * 2. 設(shè)置合理的過期時(shí)間,減少令牌泄露風(fēng)險(xiǎn)
 * 3. 從配置文件中讀取密鑰,便于管理和輪換
 * 4. 提供完整的異常處理,防止無效令牌導(dǎo)致系統(tǒng)異常
 */
@Component
public class JwtUtil {
    // 從配置文件中注入JWT密鑰,生產(chǎn)環(huán)境應(yīng)使用復(fù)雜且足夠長的密鑰
    @Value("${jwt.secret}")
    private String secret;
    // 從配置文件中注入JWT過期時(shí)間(秒)
    @Value("${jwt.expiration}")
    private long expiration;
    /**
     * 生成JWT令牌
     * 
     * 安全考慮:
     * 1. 只包含必要信息(用戶名),不包含敏感數(shù)據(jù)
     * 2. 設(shè)置簽發(fā)時(shí)間和過期時(shí)間,控制令牌有效期
     * 3. 使用強(qiáng)加密算法(HS512)進(jìn)行簽名
     * 
     * @param authentication Spring Security認(rèn)證對象
     * @return JWT令牌字符串
     */
    public String generateToken(Authentication authentication) {
        // 從認(rèn)證對象中獲取用戶信息
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Date now = new Date();
        // 計(jì)算過期時(shí)間:當(dāng)前時(shí)間 + 配置的過期時(shí)間(轉(zhuǎn)換為毫秒)
        Date expiryDate = new Date(now.getTime() + expiration * 1000);
        // 構(gòu)建JWT令牌
        return Jwts.builder()
                .setSubject(userDetails.getUsername()) // 設(shè)置主題(用戶名)
                .setIssuedAt(now)                     // 設(shè)置簽發(fā)時(shí)間
                .setExpiration(expiryDate)            // 設(shè)置過期時(shí)間
                .signWith(SignatureAlgorithm.HS512, secret) // 使用HS512算法和密鑰簽名
                .compact();                           // 生成緊湊的JWT字符串
    }
    /**
     * 從JWT令牌中提取用戶名
     * 
     * 安全考慮:
     * 1. 驗(yàn)證簽名確保令牌未被篡改
     * 2. 解析前不信任任何令牌內(nèi)容
     * 
     * @param token JWT令牌
     * @return 用戶名
     */
    public String getUsernameFromToken(String token) {
        // 解析JWT令牌,驗(yàn)證簽名并獲取聲明(Claims)
        Claims claims = Jwts.parser()
                .setSigningKey(secret)                // 設(shè)置簽名密鑰
                .parseClaimsJws(token)                // 解析JWS(已簽名的JWT)
                .getBody();                           // 獲取有效負(fù)載(Payload)
        // 返回主題(用戶名)
        return claims.getSubject();
    }
    /**
     * 驗(yàn)證JWT令牌的有效性
     * 
     * 安全考慮:
     * 1. 驗(yàn)證簽名是否正確,防止偽造令牌
     * 2. 檢查令牌是否過期
     * 3. 捕獲所有可能異常,防止無效令牌導(dǎo)致系統(tǒng)異常
     * 
     * @param token JWT令牌
     * @return 令牌是否有效
     */
    public boolean validateToken(String token) {
        try {
            // 嘗試解析令牌,如果成功則說明令牌有效
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (SignatureException ex) {
            // 簽名不匹配 - 令牌可能被篡改
            // 記錄日志但不拋出異常,避免信息泄露
        } catch (MalformedJwtException ex) {
            // 令牌格式錯誤 - 不是有效的JWT
        } catch (ExpiredJwtException ex) {
            // 令牌已過期 - 需要重新登錄獲取新令牌
        } catch (UnsupportedJwtException ex) {
            // 不支持的JWT令牌 - 可能使用了錯誤的算法
        } catch (IllegalArgumentException ex) {
            // JWT claims string is empty - 令牌為空
        }
        // 任何異常都意味著令牌無效
        return false;
    }
}

3.8 JWT認(rèn)證過濾

package com.example.demo.config;
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.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
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;
/**
 * JWT認(rèn)證過濾器 - 處理每個請求的JWT認(rèn)證
 * 
 * 安全特性說明:
 * 1. 在每個請求前執(zhí)行,確保所有請求都經(jīng)過認(rèn)證檢查
 * 2. 從Authorization頭中提取Bearer令牌
 * 3. 驗(yàn)證令牌有效性并設(shè)置安全上下文
 * 4. 即使認(rèn)證失敗也繼續(xù)過濾器鏈,確保公共接口可訪問
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 過濾器核心方法 - 處理每個HTTP請求
     * 
     * 安全流程:
     * 1. 從請求中提取JWT令牌
     * 2. 驗(yàn)證令牌有效性
     * 3. 如果有效,從令牌中提取用戶名并加載用戶詳情
     * 4. 設(shè)置安全上下文,供后續(xù)授權(quán)檢查使用
     * 
     * @param request HTTP請求
     * @param response HTTP響應(yīng)
     * @param filterChain 過濾器鏈
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            // 從HTTP請求中獲取JWT令牌
            String jwt = getJwtFromRequest(request);
            // 驗(yàn)證令牌是否存在且有效
            if (StringUtils.hasText(jwt) && jwtUtil.validateToken(jwt)) {
                // 從有效令牌中提取用戶名
                String username = jwtUtil.getUsernameFromToken(jwt);
                // 從數(shù)據(jù)庫加載用戶詳細(xì)信息(包括權(quán)限)
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // 創(chuàng)建認(rèn)證令牌,包含用戶詳情和權(quán)限
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                // 添加請求詳情(如IP地址、會話ID等)
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                // 將認(rèn)證信息設(shè)置到安全上下文中,供后續(xù)授權(quán)檢查使用
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            // 捕獲所有異常,避免因認(rèn)證問題導(dǎo)致請求失敗
            // 記錄錯誤日志但繼續(xù)處理請求(某些接口可能允許匿名訪問)
            logger.error("Could not set user authentication in security context", ex);
        }
        // 繼續(xù)過濾器鏈處理(無論認(rèn)證成功與否)
        filterChain.doFilter(request, response);
    }
    /**
     * 從HTTP請求中提取JWT令牌
     * 
     * 安全考慮:
     * 1. 只接受Bearer類型的認(rèn)證頭
     * 2. 移除"Bearer "前綴,獲取純令牌
     * 
     * @param request HTTP請求
     * @return JWT令牌或null(如果不存在)
     */
    private String getJwtFromRequest(HttpServletRequest request) {
        // 從Authorization頭獲取Bearer令牌
        String bearerToken = request.getHeader("Authorization");
        // 檢查令牌是否存在且格式正確(以"Bearer "開頭)
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            // 返回去掉"Bearer "前綴的純令牌
            return bearerToken.substring(7);
        }
        // 沒有找到有效令牌
        return null;
    }
}

3.9 Spring Security配置

package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
 * Spring Security配置類 - 定義應(yīng)用程序的安全策略
 * 
 * 安全特性說明:
 * 1. 使用無狀態(tài)會話管理,適合RESTful API
 * 2. 配置密碼編碼器,確保密碼安全存儲
 * 3. 定義URL訪問規(guī)則,實(shí)現(xiàn)基于角色的訪問控制
 * 4. 集成JWT認(rèn)證過濾器,替代默認(rèn)的表單登錄
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private JwtUtil jwtUtil;
    /**
     * 密碼編碼器Bean - 用于密碼加密和驗(yàn)證
     * 
     * 安全考慮:
     * 1. 使用BCrypt強(qiáng)哈希算法,自動處理鹽值
     * 2. 適合密碼存儲,抵抗彩虹表攻擊
     * 
     * @return BCrypt密碼編碼器實(shí)例
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 認(rèn)證管理器Bean - 暴露給其他組件使用
     * 
     * 用途:
     * 1. 在AuthController中用于手動認(rèn)證用戶
     * 2. 可以被其他需要認(rèn)證服務(wù)的組件使用
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    /**
     * 配置認(rèn)證管理器 - 設(shè)置自定義用戶詳情服務(wù)和密碼編碼器
     * 
     * 安全流程:
     * 1. 使用自定義UserDetailsService從數(shù)據(jù)庫加載用戶信息
     * 2. 使用BCrypt密碼編碼器驗(yàn)證密碼
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    /**
     * 配置HTTP安全策略 - 核心安全配置方法
     * 
     * 安全策略:
     * 1. 禁用CORS和CSRF(因使用無狀態(tài)JWT認(rèn)證)
     * 2. 使用無狀態(tài)會話管理
     * 3. 配置URL訪問規(guī)則(基于角色)
     * 4. 添加JWT認(rèn)證過濾器
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 啟用CORS并禁用CSRF(因使用JWT而非Cookie)
            .cors().and().csrf().disable()
            // 會話管理設(shè)置為無狀態(tài)(不創(chuàng)建和使用HTTP會話)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            // 配置請求授權(quán)規(guī)則
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()      // 認(rèn)證接口允許匿名訪問
                .antMatchers("/api/user/**").hasRole("USER")  // 用戶接口需要USER角色
                .antMatchers("/api/admin/**").hasRole("ADMIN") // 管理員接口需要ADMIN角色
                .anyRequest().authenticated()                 // 其他所有請求需要認(rèn)證
            .and();
        // 添加JWT認(rèn)證過濾器到UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    /**
     * 創(chuàng)建JWT認(rèn)證過濾器Bean
     * 
     * 說明:
     * 1. 過濾器在每個請求前執(zhí)行
     * 2. 負(fù)責(zé)提取和驗(yàn)證JWT令牌
     * 3. 設(shè)置安全上下文中的認(rèn)證信息
     * 
     * @return JWT認(rèn)證過濾器實(shí)例
     */
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

3.10 控制器

// AuthController.java
package com.example.demo.controller;
import com.example.demo.config.JwtUtil;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
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.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody Map<String, String> loginRequest) {
        String username = loginRequest.get("username");
        String password = loginRequest.get("password");
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(username, password)
        );
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = jwtUtil.generateToken(authentication);
        User user = userService.findByUsername(username);
        Map<String, Object> response = new HashMap<>();
        response.put("token", jwt);
        response.put("user", user);
        return ResponseEntity.ok(response);
    }
}
// TestController.java
package com.example.demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class TestController {
    @GetMapping("/user/test")
    @PreAuthorize("hasRole('USER')")
    public String userAccess() {
        return "用戶內(nèi)容";
    }
    @GetMapping("/admin/test")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminAccess() {
        return "管理員內(nèi)容";
    }
}

3.11 主應(yīng)用類

// DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3.12 測試

登錄獲取令牌:

POST http://localhost:8080/api/auth/login
Content-Type: application/json
{
  "username": "user",
  "password": "password"
}

訪問用戶API:

GET http://localhost:8080/api/user/test
Authorization: Bearer <your_token>

訪問管理員API:

GET http://localhost:8080/api/admin/test
Authorization: Bearer <your_token>

到此這篇關(guān)于Spring Security的基本使用示例的文章就介紹到這了,更多相關(guān)Spring Security使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springcloud?eureka切換nacos的配置方法

    springcloud?eureka切換nacos的配置方法

    這篇文章主要介紹了springcloud?eureka切換nacos,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01
  • Java設(shè)計(jì)模式之Strategy模式

    Java設(shè)計(jì)模式之Strategy模式

    Strategy模式即策略模式,就是將一個算法的不同實(shí)現(xiàn)封裝成一個個單獨(dú)的類,這些類實(shí)現(xiàn)同一個接口,使用者直接使用該接口來訪問具體的算法。這個樣子,使用者就可以使用不同的算法來實(shí)現(xiàn)業(yè)務(wù)邏輯了。
    2016-07-07
  • SpringMVC+Mysql實(shí)例詳解(附demo)

    SpringMVC+Mysql實(shí)例詳解(附demo)

    本篇文章主要介紹了SpringMVC+Mysql實(shí)例詳解(附demo),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。
    2016-12-12
  • Spring?Boot?中事務(wù)的用法示例詳解

    Spring?Boot?中事務(wù)的用法示例詳解

    本文詳細(xì)介紹了Spring Boot中事務(wù)管理的使用方法,包括事務(wù)的基本概念、配置、傳播行為、隔離級別以及回滾機(jī)制,通過使用@Transactional注解,可以方便地實(shí)現(xiàn)事務(wù)的控制,文章還討論了事務(wù)方法的可見性、自我調(diào)用問題以及超時(shí)設(shè)置等注意事項(xiàng),感興趣的朋友一起看看吧
    2025-02-02
  • 淺談IDEA實(shí)用的Servlet模板

    淺談IDEA實(shí)用的Servlet模板

    今天給大家分享一下IDEA實(shí)用的Servlet模板,文中有非常詳細(xì)的圖文介紹及代碼示例,對小伙伴們很有幫助哦,需要的朋友可以參考下
    2021-05-05
  • 解決try-catch捕獲異常信息后Spring事務(wù)失效的問題

    解決try-catch捕獲異常信息后Spring事務(wù)失效的問題

    這篇文章主要介紹了解決try-catch捕獲異常信息后Spring事務(wù)失效的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • idea maven編譯報(bào)錯Java heap space的解決方法

    idea maven編譯報(bào)錯Java heap space的解決方法

    這篇文章主要為大家詳細(xì)介紹了idea maven編譯報(bào)錯Java heap space的相關(guān)解決方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • 關(guān)于Spring Boot WebSocket整合以及nginx配置詳解

    關(guān)于Spring Boot WebSocket整合以及nginx配置詳解

    這篇文章主要給大家介紹了關(guān)于Spring Boot WebSocket整合以及nginx配置的相關(guān)資料,文中通過示例代碼給大家介紹的非常詳細(xì),相信對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)下吧。
    2017-09-09
  • Java同步容器和并發(fā)容器詳解

    Java同步容器和并發(fā)容器詳解

    這篇文章主要介紹了Java同步容器和并發(fā)容器詳解,容器是Java基礎(chǔ)類庫中使用頻率最高的一部分,Java集合包中提供了大量的容器類來幫組我們簡化開發(fā),下面小編和大家來一起學(xué)習(xí)下吧
    2019-06-06
  • 使用restTemplate遠(yuǎn)程調(diào)controller路徑取數(shù)據(jù)

    使用restTemplate遠(yuǎn)程調(diào)controller路徑取數(shù)據(jù)

    這篇文章主要介紹了使用restTemplate遠(yuǎn)程調(diào)controller路徑取數(shù)據(jù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論