国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

寧波做網(wǎng)站的大公司排名關鍵詞搜索推廣排行榜

寧波做網(wǎng)站的大公司排名,關鍵詞搜索推廣排行榜,上海建設官方網(wǎng)站,蘇州網(wǎng)站推廣找蘇州夢易行springboot3.x jwtspring security6.x實現(xiàn)用戶登錄認證 什么是JWT JWT(JSON Web Token)是一種開放標準(RFC 7519),它用于在網(wǎng)絡應用環(huán)境中傳遞聲明。通常,JWT用于身份驗證和信息交換。JWT的一個典型用法是…

springboot3.x jwt+spring security6.x實現(xiàn)用戶登錄認證

什么是JWT

JWT(JSON Web Token)是一種開放標準(RFC 7519),它用于在網(wǎng)絡應用環(huán)境中傳遞聲明。通常,JWT用于身份驗證和信息交換。JWT的一個典型用法是在Web應用中實現(xiàn)基于Token的身份驗證。

JWT 的特點

自包含(Self-contained):JWT 包含了用戶的認證信息和其他相關的數(shù)據(jù),因此無需在服務器端存儲會話信息。它是通過加密或簽名的方式來保證數(shù)據(jù)的完整性和防止篡改。

JWT的結(jié)構(gòu)

JWT由三部分組成:

Header(頭部):包含令牌的類型(通常是JWT)和加密算法(如HMAC SHA256或RSA)。
Payload(負載):包含聲明(Claims)。這些聲明可以是關于實體(通常是用戶)和附加數(shù)據(jù)的斷言。例如,可以包含用戶的ID、角色、權(quán)限等。
Signature(簽名):用來驗證消息的完整性以及發(fā)送者的身份。通過Header中的算法和密鑰生成簽名。

什么是spring Security

Spring Security 是一個功能強大且高度可定制的身份驗證和訪問控制框架,通常用于保護 Spring 應用程序。它提供了各種安全功能,如:

認證(Authentication):用于確認用戶的身份。例如,檢查用戶名和密碼。
授權(quán)(Authorization):用于控制用戶是否有權(quán)限訪問某些資源。通常通過角色或權(quán)限進行授權(quán)。
防護功能:提供 CSRF 防護、會話管理、跨站點請求偽造防護等安全功能。

Spring Security 是一個全面的安全框架,可以與多種身份驗證機制集成,如基于表單的登錄、OAuth2、LDAP、JWT 等。

有了spring Security為什么還需要JWT

Spring Security 本身并不負責管理用戶登錄后如何保持會話的狀態(tài),這部分通常是通過 會話Session(以前的方式) 或 JWT 來完成的。

JWT 和 spring Security關系

Spring Security 和 JWT 之間并沒有直接的關系,但是它們可以很好地結(jié)合使用。在一個典型的現(xiàn)代 Web 應用中,Spring Security 負責保護應用的安全性,而 JWT 通常用于實現(xiàn)基于 token 的無狀態(tài)認證。

Spring Boot 中的安全驗證和存儲用戶信息

在Spring Boot應用中,常用的安全驗證方式是結(jié)合 Spring Security 和 JWT。Spring Security提供了多種身份驗證機制,而JWT則用于實現(xiàn)無狀態(tài)的認證方案。JWT通常在用戶登錄成功后生成,并作為后續(xù)請求的認證憑證。

一般流程

  1. 用戶登錄:用戶通過用戶名和密碼發(fā)送請求給后端,后端驗證用戶身份。
  2. 生成JWT:身份驗證通過后,后端生成JWT,并將其發(fā)送給前端。JWT通常在HTTP響應的Authorization頭中返回。
  3. 存儲JWT:前端將JWT存儲在本地(例如localStorage或sessionStorage)或Cookie中。
  4. 后續(xù)請求:每次前端向后端發(fā)起請求時,JWT都會包含在請求頭的Authorization字段中,后端通過JWT驗證用戶身份。
  5. 驗證JWT:后端通過Spring Security中的過濾器解碼JWT,并驗證簽名。如果JWT有效,用戶身份就會被確認。

為什么說JWT是無狀態(tài)登錄

是因為它的使用方式不需要在服務器端存儲會話信息,也就是說,服務器不需要保存任何關于客戶端的狀態(tài)信息

有狀態(tài)和無狀態(tài)

有狀態(tài)認證(Stateful Authentication)

在傳統(tǒng)的認證機制中,服務器會維護一個會話(session)來記錄客戶端的身份信息。例如,在基于會話的認證中,當用戶成功登錄時,服務器會創(chuàng)建一個會話并生成一個唯一的會話 ID。這個會話 ID 會存儲在服務器端的內(nèi)存或數(shù)據(jù)庫中,且會在客戶端的瀏覽器中以 Cookie 或其他方式保存(例如,JSESSIONID)。每次客戶端發(fā)起請求時,都會攜帶會話 ID,服務器根據(jù)會話 ID 查找會話信息來確認用戶身份。

問題:服務器需要維護會話狀態(tài)。每當用戶發(fā)送請求時,服務器需要查詢存儲的會話數(shù)據(jù)。這會增加服務器的負擔,尤其是在分布式系統(tǒng)中,需要將會話數(shù)據(jù)共享或同步到不同的服務器節(jié)點。

無狀態(tài)認證(Stateless Authentication)

與會話認證不同,無狀態(tài)認證不需要在服務器上存儲任何客戶端的會話信息。服務器驗證身份時,通過客戶端攜帶的信息(如 JWT)來驗證請求,而不依賴于服務器端存儲的會話狀態(tài)。JWT 本身包含了足夠的信息來完成身份驗證和授權(quán),服務器只需解析和驗證這個 token,而無需查詢?nèi)魏未鎯Φ男畔ⅰ?/p>

為什么不手動封裝json存儲

數(shù)據(jù)完整性與安全性

JWT: JWT 的一個核心特性是 簽名,它確保了數(shù)據(jù)的 完整性和安全性。JWT 中的簽名是由服務器的私鑰生成的,任何人如果修改了 JWT 的內(nèi)容,簽名就會變得無效,服務器就能發(fā)現(xiàn)數(shù)據(jù)被篡改。
例如,如果你將用戶信息存儲為 JSON,前端將其發(fā)送給服務器,那么攻擊者可能會篡改這個 JSON 數(shù)據(jù)(如修改用戶角色、權(quán)限等),而服務器無法判斷數(shù)據(jù)是否被篡改,除非你做額外的安全處理(比如加密)。
自定義 JSON: 如果你讓前端封裝 JSON 并發(fā)送到服務器,服務器將無法知道數(shù)據(jù)是否被篡改。沒有類似 JWT 簽名的機制,前端發(fā)送的 JSON 數(shù)據(jù)就很容易被篡改。為了解決這個問題,你需要自己實現(xiàn)一些安全機制(如加密或自定義簽名),但這會增加實現(xiàn)的復雜度。

無狀態(tài)性與有效期管理

JWT:JWT 是自包含的,它不僅僅包含用戶信息,還可以包含 過期時間(exp)等信息。這樣,服務器可以非常容易地判斷一個 JWT 是否過期,無需查詢數(shù)據(jù)庫或任何狀態(tài)。

自定義 JSON:如果前端僅僅發(fā)送一個 JSON 數(shù)據(jù)包,服務器就無法知道該數(shù)據(jù)是否已經(jīng)過期。為了實現(xiàn)這一點,你需要自己維護一個機制,比如在 JSON 中加入時間戳,或者在服務器端保存用戶的會話過期時間。這會導致服務器變得有狀態(tài),因為服務器需要管理每個用戶的會話生命周期。

服務器性能與分布式架構(gòu)支持

JWT:由于 JWT 是自包含的,所有的信息都在 token 中,服務器不需要存儲任何會話信息,這意味著它是 無狀態(tài)的。無狀態(tài)性在 分布式架構(gòu) 中特別有優(yōu)勢,因為多個微服務可以獨立地驗證 JWT 而無需共享會話數(shù)據(jù)。

自定義 JSON:如果你選擇自己封裝 JSON,并由前端存儲和發(fā)送,每次請求時,服務器就需要檢查和驗證這個 JSON 信息。尤其是在分布式環(huán)境中,你可能需要通過共享會話存儲(如 Redis)來確保各個服務都能正確驗證用戶身份,增加了管理的復雜性。

可擴展性

JWT:由于 JWT 是標準化的,支持的庫和工具非常多,你可以很容易地實現(xiàn)不同平臺(如前端、后端、移動端等)之間的認證,而且不需要太多的額外工作。JWT 自帶的靈活性和規(guī)范使得它可以適應多種需求。

自定義 JSON:如果你手動封裝 JSON 并且實現(xiàn)自己的認證機制,雖然在小范圍內(nèi)可以工作,但這種方法缺乏標準化和統(tǒng)一性,擴展性差。在系統(tǒng)規(guī)模擴大或需求變化時,可能會遇到很多維護和兼容性問題。

實現(xiàn)

1.引入依賴

引入JWT依賴
        <!-- JWT library --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.11.2</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.2</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.2</version><scope>runtime</scope></dependency>
引入spring Security依賴
        <!-- Spring Security for Authentication --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

2.實現(xiàn) UserDetailsService接口

UserDetailsService 是 Spring Security 中一個非常重要的接口,負責從數(shù)據(jù)庫或其他存儲中加載用戶的詳細信息,通常用于身份驗證和授權(quán)過程。

在 Spring Security 中,用戶信息通常存儲在數(shù)據(jù)庫或其他外部系統(tǒng)中。為了進行認證和授權(quán),Spring Security 需要從這些存儲中獲取用戶的詳細信息,如用戶名、密碼、角色、權(quán)限等。為了實現(xiàn)這一點,Spring Security 提供了 UserDetailsService 接口,它定義了如何加載這些用戶信息。

package com.example.demonew.service.loginService;import com.example.demonew.entity.UserInfo;
import com.example.demonew.mapper.loginMapper.LoginMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.Map;@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate LoginMapper loginMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {UserInfo user = loginMapper.getUserInfo(username);if (user == null) {throw new UsernameNotFoundException("User not found");}//將信息添加到User也可以說是UserDetails 對象中return User.withUsername(user.getUsername()).password(user.getPassword()).roles(user.getRole()).build();}}
總結(jié)

實現(xiàn) UserDetailsService 接口的主要目的是將用戶的認證信息(包括用戶名、密碼、角色等)從數(shù)據(jù)庫等存儲中提取出來,并轉(zhuǎn)化為 Spring Security 能夠理解并使用的 UserDetails 對象。在 Spring Security 的認證和授權(quán)流程中,UserDetailsService 是用來提供用戶信息的核心組件。

當用戶嘗試登錄時,Spring Security 會調(diào)用 loadUserByUsername 方法來查找用戶,并使用返回的 UserDetails 來進行身份驗證和授權(quán)。因此,實現(xiàn) UserDetailsService 是將自定義的用戶數(shù)據(jù)源(如數(shù)據(jù)庫)與 Spring Security 進行集成的關鍵步驟。

3.實現(xiàn)JWT工具類

這里工具類實現(xiàn)了,生成jwt token,解密jwt, 以及通過jwt獲取用戶名,驗證令牌等功能,方便后續(xù)調(diào)用

package com.example.demonew.util;import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.security.Key;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;@Component
@Slf4j
public class JwtTokenProviderUtil {@Value("${app.jwt-secret}")private String jwtSecret;@Value("${app.jwt-expiration-milliseconds}")private long jwtExpirationDate;private Key key;// 使用懶加載方式獲取密鑰private Key getKey() {if (key == null) {synchronized (this) {if (key == null) {key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret));}}}return key;}// 生成 JWT tokenpublic String generateToken(Authentication authentication){String username = authentication.getName();Date currentDate = new Date();Date expireDate = new Date(currentDate.getTime() + jwtExpirationDate);String token = Jwts.builder().setSubject(username).setIssuedAt(new Date()).setExpiration(expireDate).signWith(getKey(),SignatureAlgorithm.HS256).compact();return token;}private Key key(){return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret));}//解密tokenpublic String resolveToken(HttpServletRequest request) {String bearerToken = request.getHeader("Authorization");if (bearerToken != null && bearerToken.startsWith("Bearer ")) {return bearerToken.substring(7);}return null;}// 解析 JWT 令牌并返回其主體private Claims getClaimsFromToken(String token) {try {return Jwts.parserBuilder().setSigningKey(getKey()).build().parseClaimsJws(token).getBody();} catch (JwtException e) {log.error("Error parsing JWT token: {}", e.getMessage());throw new RuntimeException("JWT token parsing failed", e);}}// 驗證 JWT 令牌public boolean validateToken(String token) {try{Jwts.parserBuilder().setSigningKey(getKey()).build().parseClaimsJws(token);return true;} catch (MalformedJwtException e) {log.error("Invalid JWT token: {}", e.getMessage());} catch (ExpiredJwtException e) {log.error("JWT token is expired: {}", e.getMessage());} catch (UnsupportedJwtException e) {log.error("JWT token is unsupported: {}", e.getMessage());} catch (IllegalArgumentException e) {log.error("JWT claims string is empty: {}", e.getMessage());}return false;}// 獲取用戶認證信息public Authentication getAuthentication(String token) {String username = getUsername(token);List<GrantedAuthority> authorities = getAuthorities(token);return new UsernamePasswordAuthenticationToken(username, "", authorities);}//通過jwt信息獲取用戶名public String getUsername(String token) {Claims claims = getClaimsFromToken(token);String username = claims.getSubject();return username;}public List<GrantedAuthority> getAuthorities(String token) {Claims claims = getClaimsFromToken(token);List<String> roles = (List<String>)claims.get("roles");return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}}
設置密鑰和過期時間
app:jwt-secret: 60fosjWhlsy6bxLjSv5P/8fvmvanEEAjUQm3KLkSuMc= # 密鑰 不唯一自己設置jwt-expiration-milliseconds: 604800000
隨機生成密鑰
    @Testvoid jwtSecretGenerator (){SecureRandom random = new SecureRandom();byte[] secret = new byte[32];  // 256 位random.nextBytes(secret);String jwtSecret = Base64.getEncoder().encodeToString(secret);  // 將字節(jié)數(shù)組轉(zhuǎn)換為 Base64 編碼字符串System.out.println(jwtSecret);}

4.spring Security配置類

Spring Security 配置類主要用于設置安全策略,過濾器鏈以及禁用默認的 CSRF 等。
spring boot3.0廢棄了extends WebSecurityConfigurerAdapter的方式,所以這里采用添加@Bean新方式

SecurityConfig 是 Spring Security 的配置類,作用是配置 Spring Security 的安全策略,進行身份認證和授權(quán)管理

可以看出spring Security就是通過http來做跨域,權(quán)限等控制

package com.example.demonew.config;import com.example.demonew.service.loginService.CustomUserDetailsService;
import jakarta.annotation.Resource;
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.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
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  // 確保添加這個注解來啟用 Spring Security 配置
public class SecurityConfig {@Resourceprivate CustomUserDetailsService userDetailsService;@Autowiredprivate  JwtAuthenticationFilter jwtAuthenticationFilter;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {// 配置 HTTP 安全策略http.csrf(csrf -> csrf.disable()) // 由于你使用的是前后端分離的架構(gòu),CSRF 防護通常是不需要的。禁用 CSRF 防護可以避免 Spring Security 對跨站請求偽造攻擊的保護,從而簡化 API 調(diào)用。//授權(quán)配置.authorizeHttpRequests(authz -> authz.requestMatchers("/login").permitAll() // 允許所有用戶訪問 /user/login 路徑下的資源(通常是登錄接口)。其他接口需要認證后訪問。.requestMatchers("/test/**").hasAuthority("ROLE_ADMIN") // 僅允許具有 ADMIN 角色的用戶訪問 /test/** 路徑下的資源。.anyRequest().authenticated() //所有其他請求都需要進行身份認證才能訪問。)//  jwtAuthenticationFilter是一個自定義的過濾器,它負責處理請求中的 JWT 認證。此過濾器被配置為在 Spring Security 自帶的 UsernamePasswordAuthenticationFilter 之前執(zhí)行,這意味著在 Spring Security 處理用戶名和密碼認證之前,會先進行 JWT 校驗。.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class).userDetailsService(userDetailsService) //配置自定義的 UserDetailsService 用于加載用戶信息.formLogin(form -> form.disable()) // Spring Security 默認啟用基于表單的登錄認證。如果你使用的是前后端分離的方式,通常不會使用傳統(tǒng)的表單登錄,因此禁用它。.logout(logout -> logout.permitAll()); // 配置登出功能,允許所有用戶進行登出。return http.build();  // 返回配置好的 SecurityFilterChain}/*這個方法配置了 Spring Security 的 AuthenticationManager,它是進行用戶認證的核心組件。配置了一個 DaoAuthenticationProvider,它使用 CustomUserDetailsService 來加載用戶信息,使用 BCryptPasswordEncoder 進行密碼驗證。DaoAuthenticationProvider:是 Spring Security 用于基于數(shù)據(jù)庫的用戶認證提供程序,它會從 UserDetailsService 中加載用戶信息,并使用密碼編碼器對用戶密碼進行校驗。UserDetailsService:CustomUserDetailsService 實現(xiàn)了 UserDetailsService 接口,用于加載用戶信息(例如,用戶名、密碼、角色等)。它通常會從數(shù)據(jù)庫中查詢用戶信息。PasswordEncoder:BCryptPasswordEncoder 用于對用戶輸入的密碼進行加密,然后與存儲在數(shù)據(jù)庫中的加密密碼進行對比。BCrypt 是一種常見的密碼加密算法。返回 AuthenticationManager:該管理器負責執(zhí)行認證流程,返回經(jīng)過認證的 Authentication 對象。*/@Beanpublic AuthenticationManager authenticationManager(UserDetailsService userDetailsService,PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(userDetailsService); // 配置 UserDetailsService,用于加載用戶信息authenticationProvider.setPasswordEncoder(passwordEncoder); // 配置密碼編碼器,用于密碼校驗return new ProviderManager(authenticationProvider); // 返回一個 ProviderManager,管理多個認證提供者}/*該方法返回一個 BCryptPasswordEncoder 實例,用于對用戶的密碼進行加密和驗證。BCryptPasswordEncoder 是一種安全的密碼加密算法,Spring Security 默認推薦使用這種加密方式。*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}//    @Bean
//    public SecurityContextRepository securityContextRepository() {
//        return new HttpSessionSecurityContextRepository();
//    }}

5.創(chuàng)建JWT 過濾器

創(chuàng)建一個過濾器來攔截每個請求,提取 JWT,并驗證它。過濾器會檢查請求頭是否帶有 JWT,如果有,它會驗證 JWT 并通過 Spring Security 認證上下文設置用戶身份。

JwtAuthenticationFilter 是一個用于處理 JWT(JSON Web Token)身份驗證的過濾器類,繼承自 Spring Security 的 OncePerRequestFilter。它的作用是在每次請求時,檢查請求頭中的 JWT token,并進行校驗,如果 token 驗證通過,則根據(jù) token 中的信息設置 Spring Security 的 Authentication,確保用戶能夠通過 JWT 認證來訪問受保護的資源。

package com.example.demonew.config;import com.example.demonew.service.loginService.LoginService;
import com.example.demonew.util.JwtTokenProviderUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;@Component
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtTokenProviderUtil jwtTokenProvider;@Autowiredprivate UserDetailsService userDetailsService;/*這是過濾器的核心方法,它會在每次 HTTP 請求時被調(diào)用。方法中的處理流程如下:*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {// 通過 getTokenFromRequest(request) 方法,嘗試從 HTTP 請求的 Authorization 頭中提取 JWT。如果 token 存在且以 Bearer 開頭,則提取出 token 部分。String token = getTokenFromRequest(request);/* 校驗 token使用 jwtTokenProvider.validateToken(token) 校驗 JWT 的合法性。例如,檢查 token 是否過期、是否篡改等。如果 token 無效或過期,認證過程會被跳過,后續(xù)請求會被拒絕。*/if(StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)){try {// 從 token 獲取 usernameString username = jwtTokenProvider.getUsername(token);// 加載與 token 關聯(lián)的用戶UserDetails userDetails = userDetailsService.loadUserByUsername(username);/** 創(chuàng)建一個 UsernamePasswordAuthenticationToken 實例,傳入 userDetails 和該用戶的權(quán)限(userDetails.getAuthorities())。* UsernamePasswordAuthenticationToken 是 Spring Security 用來封裝用戶身份信息的一個對象,它包含了用戶的身份信息和權(quán)限。* */UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));/** 使用 SecurityContextHolder.getContext().setAuthentication(authenticationToken) 將身份認證信息(即 authenticationToken)存儲在 Spring Security 的上下文中。* 這樣,Spring Security 在處理后續(xù)請求時,就能基于這個認證信息對請求進行授權(quán)控制。* */SecurityContextHolder.getContext().setAuthentication(authenticationToken);}catch (Exception e){// 記錄日志并清除認證上下文log.warn("JWT Token validation failed: " + e.getMessage());SecurityContextHolder.clearContext();}}else {// 無 token 或 token 無效,清除認證上下文logger.warn("JWT Token is missing or invalid");SecurityContextHolder.clearContext();}/*最后,調(diào)用 filterChain.doFilter(request, response) 讓請求繼續(xù)傳遞給下一個過濾器或最終的目標(如 Controller)。這一步是確保請求能夠正常進入應用的下一階段*/filterChain.doFilter(request, response);}// 從請求頭獲取 JWT 格式為:Authorization: Bearer <token>private String getTokenFromRequest(HttpServletRequest request) {String bearerToken = request.getHeader("Authorization");if (bearerToken != null && bearerToken.startsWith("Bearer ")) {return bearerToken.substring(7); // 去掉 "Bearer " 前綴}return null;}
}
為什么使用 OncePerRequestFilter

OncePerRequestFilter 是 Spring 提供的一個過濾器基類,確保每次請求只會調(diào)用一次過濾器的 doFilterInternal 方法,而不是每個過濾器鏈都執(zhí)行一次。這可以有效避免重復執(zhí)行相同的邏輯,保證 JWT 校驗只執(zhí)行一次。

創(chuàng)建登錄接口

這樣用戶先通過用戶名,密碼進行登錄,然后校驗信息,返回userDetail對象

package com.example.demonew.controller.login;import com.example.demonew.common.Result;
import com.example.demonew.config.SecurityConfig;
import com.example.demonew.service.loginService.LoginService;
import com.example.demonew.util.JwtTokenProviderUtil;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
@RequestMapping("/user")
public class LoginController {@Autowiredprivate LoginService loginService;@Autowiredprivate JwtTokenProviderUtil jwtTokenProviderUtil;@Autowiredprivate AuthenticationManager authenticationManager;@PostMapping("/login")public Result Login(@RequestBody LoginRequest loginRequest){loginService.login(loginRequest.getUsername(),loginRequest.getPassword());// 登錄授權(quán)Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(),loginRequest.getPassword()));// 將登錄用戶信息交給SpringSecurity管理SecurityContextHolder.getContext().setAuthentication(authentication);// 利用用戶授權(quán)信息生成JWT令牌String token = jwtTokenProviderUtil.generateToken(authentication);return Result.success(token);};@PostMapping("/register")public Result registerUser(@RequestBody LoginRequest loginRequest) {BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();String encodedPassword = bCryptPasswordEncoder.encode(loginRequest.password);String password=encodedPassword;loginService.register(loginRequest.getUsername(),password);return new Result();}// 登錄請求體的封裝類public static class LoginRequest {private String username;private String password;// getters and setterspublic String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}}

準備數(shù)據(jù)

在這里插入圖片描述
密碼這里先手動生成一個

    @Testvoid testBCryptPasswordEncoder(){BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String encode = encoder.encode("123456");System.out.println(encode);}

postman測試

登錄

登錄不需要帶Authorization信息,登錄后jwt會生成token返回,后續(xù)請求需要帶上
在這里插入圖片描述

其他接口

沒權(quán)限 未帶認證
在這里插入圖片描述

帶上認證成功訪問
在這里插入圖片描述

錯誤總結(jié)

執(zhí)行filterChain.doFilter(request, response);后無法進入controller

解決

檢查securityFilterChain方法中的requestMatchers方法,url是否正確
requestMatchers(“/user/login”).permitAll() // 開放登錄接口

Caused by: java.lang.IllegalArgumentException: A UserDetailsService must be set

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'securityFilterChain' defined in class path resource [com/example/demonew/config/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'securityFilterChain' threw exception with message: Could not postProcess org.springframework.security.authentication.dao.DaoAuthenticationProvider@2a4e939a of type class org.springframework.security.authentication.dao.DaoAuthenticationProviderat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:896)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:849)at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:146)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:509)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1441)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:289)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1122)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1093)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1030)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)at com.example.demonew.DemoNewApplication.main(DemoNewApplication.java:16)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityFilterChain' defined in class path resource [com/example/demonew/config/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'securityFilterChain' threw exception with message: Could not postProcess org.springframework.security.authentication.dao.DaoAuthenticationProvider@2a4e939a of type class org.springframework.security.authentication.dao.DaoAuthenticationProviderat org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:657)at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:645)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1357)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:289)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1883)at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1847)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1737)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1705)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1580)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1519)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:888)... 22 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'securityFilterChain' threw exception with message: Could not postProcess org.springframework.security.authentication.dao.DaoAuthenticationProvider@2a4e939a of type class org.springframework.security.authentication.dao.DaoAuthenticationProviderat org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:199)at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiateWithFactoryMethod(SimpleInstantiationStrategy.java:88)at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:168)at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)... 39 common frames omitted
Caused by: java.lang.RuntimeException: Could not postProcess org.springframework.security.authentication.dao.DaoAuthenticationProvider@2a4e939a of type class org.springframework.security.authentication.dao.DaoAuthenticationProviderat org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor.postProcess(AutowireBeanFactoryObjectPostProcessor.java:71)at org.springframework.security.config.annotation.SecurityConfigurerAdapter$CompositeObjectPostProcessor.postProcess(SecurityConfigurerAdapter.java:130)at org.springframework.security.config.annotation.SecurityConfigurerAdapter.postProcess(SecurityConfigurerAdapter.java:82)at org.springframework.security.config.annotation.authentication.configurers.userdetails.AbstractDaoAuthenticationConfigurer.configure(AbstractDaoAuthenticationConfigurer.java:96)at org.springframework.security.config.annotation.authentication.configurers.userdetails.AbstractDaoAuthenticationConfigurer.configure(AbstractDaoAuthenticationConfigurer.java:36)at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.configure(AbstractConfiguredSecurityBuilder.java:398)at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:352)at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:38)at org.springframework.security.config.annotation.web.builders.HttpSecurity.beforeConfigure(HttpSecurity.java:3301)at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:351)at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:38)at com.example.demonew.config.SecurityConfig.securityFilterChain(SecurityConfig.java:49)at com.example.demonew.config.SecurityConfig$$SpringCGLIB$$0.CGLIB$securityFilterChain$2(<generated>)at com.example.demonew.config.SecurityConfig$$SpringCGLIB$$FastClass$$1.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:348)at com.example.demonew.config.SecurityConfig$$SpringCGLIB$$0.securityFilterChain(<generated>)at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)at java.base/java.lang.reflect.Method.invoke(Method.java:580)at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:171)... 42 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.authentication.dao.DaoAuthenticationProvider@2a4e939a': A UserDetailsService must be setat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1808)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:413)at org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor.initializeBeanIfNeeded(AutowireBeanFactoryObjectPostProcessor.java:98)at org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor.postProcess(AutowireBeanFactoryObjectPostProcessor.java:67)... 61 common frames omitted
Caused by: java.lang.IllegalArgumentException: A UserDetailsService must be setat org.springframework.util.Assert.notNull(Assert.java:181)at org.springframework.security.authentication.dao.DaoAuthenticationProvider.doAfterPropertiesSet(DaoAuthenticationProvider.java:99)at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.afterPropertiesSet(AbstractUserDetailsAuthenticationProvider.java:119)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)... 64 common frames omitted
Disconnected from the target VM, address: '127.0.0.1:59351', transport: 'socket'Process finished with exit code 1
解決

2025-01-14 11:59:16.898 WARN — [nio-8080-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt

解決

密碼格式不正確,確保數(shù)據(jù)庫中的密碼使用 BCrypt 加密

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String encode = encoder.encode("123456");

TemplateInputException

詳細信息如下

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [user/login], template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [user/login], template might not exist or might not be accessible by any of the configured Template Resolvers

在返回return時候報錯

解決

在 pom.xml 中刪除 Thymeleaf 依賴(如果不使用 Thymeleaf)

原因

Spring Boot 默認渲染視圖:

當你使用 Spring Boot 啟動項目時,如果沒有明確聲明返回類型,Spring Boot 會默認嘗試將響應返回為視圖(view)頁面,而不是直接返回 JSON 或其他格式的數(shù)據(jù)。
在你的代碼中,login 方法返回的是 Result.success(token),這個 token 是你生成的 JWT,應該作為 API 的響應數(shù)據(jù)返回,而不是試圖通過 Thymeleaf 渲染 user/login 模板。
返回的是數(shù)據(jù),而非視圖:

如果你的 login 是一個 RESTful 風格的 API,應該返回 JSON 數(shù)據(jù)而不是試圖渲染頁面。Spring 默認會將沒有指定視圖的請求作為視圖解析,導致它去找 user/login.html。

http://m.aloenet.com.cn/news/44103.html

相關文章:

  • 如何免費網(wǎng)站建設怎么可以在百度發(fā)布信息
  • 做網(wǎng)站運營的女生多嗎百度公司簡介
  • 上海小微企業(yè)名錄查詢seo短視頻網(wǎng)頁入口營銷
  • 公司網(wǎng)站建設北京北京做百度推廣的公司
  • 網(wǎng)站開發(fā)用啥語言廣州各區(qū)正在進一步優(yōu)化以下措施
  • 順德做網(wǎng)站公司seo搜狗
  • 網(wǎng)站后端怎么做河北網(wǎng)站建設公司排名
  • 手機模板網(wǎng)站模板鞏義網(wǎng)站優(yōu)化公司
  • 網(wǎng)頁的動態(tài)效果網(wǎng)店關鍵詞怎么優(yōu)化
  • 自己做網(wǎng)站需要服務器培訓機構(gòu)查詢網(wǎng)
  • 自助申請海外網(wǎng)站長沙網(wǎng)絡公關公司
  • 傳統(tǒng)營銷渠道有哪些seo網(wǎng)站排名優(yōu)化培訓教程
  • 網(wǎng)站建設及推廣銷售話術新app推廣方案
  • 衡陽公司做網(wǎng)站關鍵詞分類
  • 北京好的網(wǎng)站開發(fā)網(wǎng)站推廣 方法
  • 北京十佳網(wǎng)站建設廣告網(wǎng)站大全
  • 唐山住房和城鄉(xiāng)建設廳網(wǎng)站谷歌外貿(mào)seo
  • 景區(qū)網(wǎng)站建設教程如何免費發(fā)布廣告
  • 網(wǎng)站開發(fā)公司總匯seo基礎知識培訓視頻
  • 購物商城開發(fā)seo優(yōu)化設計
  • 大連重工 央企江西seo推廣軟件
  • 音樂網(wǎng)站開發(fā)的目的杭州百度推廣代理公司哪家好
  • 網(wǎng)站建設教程 pdf營銷型網(wǎng)站建設論文
  • 電子商務網(wǎng)站建設模板代碼互聯(lián)網(wǎng)廣告銷售是做什么的
  • WordPress的目錄大綱杭州百度快照優(yōu)化排名
  • 高密市網(wǎng)站建設鄭州網(wǎng)站seo顧問
  • logo免費設計無水印seo搜索工具欄
  • 山東政務網(wǎng)站建設seo和sem的聯(lián)系
  • 無錫網(wǎng)站建設 app百度的搜索引擎優(yōu)化
  • 網(wǎng)站備案查詢api逆冬seo