最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Boot基于?JWT?優(yōu)化?Spring?Security?無狀態(tài)登錄實戰(zhàn)指南

 更新時間:2025年11月27日 11:26:51   作者:Java天梯之路  
本文介紹如何使用JWT優(yōu)化SpringSecurity實現(xiàn)無狀態(tài)登錄,提高接口安全性,并通過實際操作步驟展示了如何配置JWT參數(shù)、實現(xiàn)JWT登錄接口、認證過濾器等,感興趣的朋友跟隨小編一起看看吧

Spring Boot 實戰(zhàn):基于 JWT 優(yōu)化 Spring Security 無狀態(tài)登錄

在上一篇文章中,我們通過 Spring Security 實現(xiàn)了基礎(chǔ)的接口權(quán)限控制,但采用的 HTTP Basic 登錄存在明顯缺陷:安全性,用戶名和密碼只是簡單的通過 Base64 編碼之后就開始傳送了,很容易被破解,進而暴露用戶信息。

本文將引入 JWT(JSON Web Token) 技術(shù),重構(gòu)登錄流程,讓接口即安全又貼合企業(yè)級項目需求。

一、先搞懂:為什么需要 JWT?

Jwt的核心優(yōu)勢是 無狀態(tài)

  • 登錄成功后,服務(wù)器生成一個包含用戶信息和權(quán)限的加密 Token,直接返回給客戶端;
  • 客戶端后續(xù)請求時,只需在請求頭攜帶 Token,服務(wù)器無需存儲任何狀態(tài),直接通過 Token 驗證身份和權(quán)限;
  • 多臺服務(wù)器共用一套 Token 驗證邏輯,無需同步狀態(tài),完美適配分布式部署。

二、準(zhǔn)備工作:添加 JWT 依賴

pom.xml<dependencies> 標(biāo)簽中,新增 JWT 相關(guān)依賴(基于 JJWT 框架,Spring 官方推薦):

        <!-- JWT 核心依賴(JJWT) -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <!-- JWT 實現(xiàn)依賴 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <!-- JWT 加密算法依賴 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>

添加后點擊 IDEA 右上角的 “Load Maven Changes” 刷新依賴,確保無紅色報錯。

三、第一步:實現(xiàn) JWT 工具類(核心)

JWT 的核心操作包括 生成 Token驗證 Token、解析 Token 中的用戶信息,我們創(chuàng)建一個工具類封裝這些邏輯,方便后續(xù)調(diào)用。

com.example.firstspringbootproject.utils 包下創(chuàng)建 JwtUtil 類:

package com.example.firstspringbootproject.utils;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtil {
    // 1. 從配置文件讀取 JWT 關(guān)鍵參數(shù)(避免硬編碼)
    @Value("${jwt.secret}") // 密鑰(必須保密,建議在生產(chǎn)環(huán)境用環(huán)境變量配置)
    private String secret;
    @Value("${jwt.expiration}") // Token 過期時間(單位:毫秒,這里配置 2 小時)
    private long expiration;
    @Value("${jwt.header}") // 請求頭中攜帶 Token 的字段名(如 Authorization)
    private String tokenHeader;
    @Value("${jwt.prefix}") // Token 前綴(如 Bearer,規(guī)范要求加空格)
    private String tokenPrefix;
    // 2. 生成 Token:基于用戶信息(用戶名、角色)
    public String generateToken(UserDetails userDetails) {
        // 存儲 Token 中的自定義信息(Payload)
        Map<String, Object> claims = new HashMap<>();
        // 將用戶角色存入 Token(后續(xù)驗證權(quán)限用)
        claims.put("roles", userDetails.getAuthorities().stream()
                .map(authority -> authority.getAuthority())
                .toList());
        // 構(gòu)建 Token 并返回
        return Jwts.builder()
                .setClaims(claims) // 自定義信息
                .setSubject(userDetails.getUsername()) // 用戶名(唯一標(biāo)識)
                .setIssuedAt(new Date()) // 簽發(fā)時間
                .setExpiration(new Date(System.currentTimeMillis() + expiration)) // 過期時間
                .signWith(SignatureAlgorithm.HS512, secret) // 加密算法(HS512)+ 密鑰
                .compact();
    }
    // 3. 從 Token 中獲取用戶名
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }
    // 4. 驗證 Token 是否有效(未過期 + 用戶名匹配)
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        // 驗證邏輯:用戶名一致 + Token 未過期 + Token 未被篡改
        return username.equals(userDetails.getUsername())
                && !isTokenExpired(token);
    }
    // 5. 從 Token 中獲取自定義角色信息
    public String getRoleFromToken(String token) {
        Claims claims = getAllClaimsFromToken(token);
        // 從自定義信息中獲取角色列表(這里簡化為單個角色,多角色可返回 List)
        return ((List<String>) claims.get("roles")).get(0);
    }
    // ------------------------------
    // 以下是內(nèi)部工具方法(無需外部調(diào)用)
    // ------------------------------
    // 解析 Token,獲取所有自定義信息(Payload)
    private Claims getAllClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret) // 用密鑰解密
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException | MalformedJwtException | SignatureException
                 | IllegalArgumentException | UnsupportedJwtException e) {
            // 捕獲 Token 異常(過期、格式錯誤、簽名錯誤等)
            throw new RuntimeException("無效的 Token:" + e.getMessage());
        }
    }
    // 從 Token 中獲取指定信息(通用方法)
    private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }
    // 判斷 Token 是否過期
    private boolean isTokenExpired(String token) {
        final Date expirationDate = getClaimFromToken(token, Claims::getExpiration);
        return expirationDate.before(new Date());
    }
    // 6. 輔助方法:從請求頭中提取 Token(去除前綴)
    public String extractTokenFromHeader(String header) {
        if (header != null && header.startsWith(tokenPrefix)) {
            // 例如:header = "Bearer eyJhbGciOiJIUzI1NiJ9...",返回后面的 Token 部分
            return header.substring(tokenPrefix.length()).trim();
        }
        return null;
    }
    // Getter 方法(供外部獲取配置參數(shù))
    public String getTokenHeader() {
        return tokenHeader;
    }
    public String getTokenPrefix() {
        return tokenPrefix;
    }
}

四、第二步:配置 JWT 參數(shù)(避免硬編碼)

src/main/resources/application.yml(或 application.properties)中,添加 JWT 相關(guān)配置(替換硬編碼,方便后續(xù)修改):

# JWT 配置
jwt:
  secret: firstspringbootproject2025secretkeyfirstspringbootproject2025secretkeyfirstspringbootproject2025secretkey # 密鑰(生產(chǎn)環(huán)境建議用 64 位以上隨機字符串)
#  expiration: 7200000 # Token 過期時間(7200000 毫秒 = 2 小時)
  expiration: 1000 # Token 過期時間(1000 毫秒 = 1 秒)
  header: Authorization # 請求頭字段名
  prefix: Bearer  # Token 前綴(注意末尾有空格)

五、第三步:實現(xiàn) JWT 登錄接口(替換 HTTP Basic)

HTTP Basic 登錄是通過瀏覽器彈窗輸入賬號密碼,體驗較差;我們需要自定義一個 登錄接口(如 /api/login),客戶端通過 JSON 提交賬號密碼,服務(wù)器驗證通過后返回 JWT Token。

1. 創(chuàng)建登錄請求參數(shù)實體類

com.example.firstspringbootproject.dto 包下創(chuàng)建 LoginRequestDTO(接收客戶端提交的賬號密碼):

package com.example.firstspringbootproject.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(name = "LoginRequestDTO", description = "登錄請求參數(shù)")
public class LoginRequestDTO {
    @NotBlank(message = "用戶名不能為空")
    @Schema(description = "用戶名", example = "admin")
    private String username;
    @NotBlank(message = "密碼不能為空")
    @Schema(description = "密碼", example = "123456")
    private String password;
}

2. 創(chuàng)建登錄控制器

com.example.firstspringbootproject.controller 包下創(chuàng)建 AuthController,實現(xiàn)登錄接口:

package com.example.firstspringbootproject.controller;
import com.example.firstspringbootproject.dto.LoginRequestDTO;
import com.example.firstspringbootproject.common.Result;
import com.example.firstspringbootproject.utils.JwtUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
@Tag(name = "認證接口", description = "登錄、Token 相關(guān)接口")
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager; // Spring Security 認證管理器
    @Autowired
    private JwtUtil jwtUtil; // 自定義 JWT 工具類
    /**
     * 登錄接口:接收賬號密碼,驗證通過后返回 JWT Token
     */
    @PostMapping("/login")
    @Operation(summary = "用戶登錄", description = "提交用戶名和密碼,獲取 JWT Token")
    public Result<Map<String, String>> login(@Valid @RequestBody LoginRequestDTO loginRequest) {
        // 1. 調(diào)用 Spring Security 認證管理器,驗證賬號密碼
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );
        // 2. 認證通過:從認證結(jié)果中獲取用戶信息
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        // 3. 生成 JWT Token
        String token = jwtUtil.generateToken(userDetails);
        // 4. 構(gòu)建返回結(jié)果(包含 Token 和過期提示)
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("token", token);
        resultMap.put("expiration", "Token 有效期 2 小時,請及時刷新");
        resultMap.put("role", jwtUtil.getRoleFromToken(token)); // 返回用戶角色,方便前端處理
        return Result.success(resultMap);
    }
}

六、第四步:實現(xiàn) JWT 認證過濾器(核心攔截邏輯)

客戶端登錄成功后,后續(xù)請求會在 Authorization 頭中攜帶 Token(格式:Bearer eyJhbGciOiJIUzI1NiJ9...)。我們需要自定義一個 過濾器,在請求到達接口前攔截 Token,完成以下操作:

  • 從請求頭中提取 Token;
  • 驗證 Token 有效性;
  • 從 Token 中解析用戶信息和角色;
  • 將用戶信息存入 Spring Security 上下文,讓后續(xù)權(quán)限校驗生效。

com.example.firstspringbootproject.filter 包下創(chuàng)建 JwtAuthenticationFilter 類:

package com.example.firstspringbootproject.filter;
import com.example.firstspringbootproject.utils.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
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.web.filter.OncePerRequestFilter;
import java.io.IOException;
// 自定義 JWT 認證過濾器:每次請求都會執(zhí)行(OncePerRequestFilter 確保一次請求只執(zhí)行一次)
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserDetailsService userDetailsService; // 之前實現(xiàn)的用戶查詢服務(wù)
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain
    ) throws ServletException, IOException {
        try {
            // 1. 從請求頭中提取 Token
            String token = jwtUtil.extractTokenFromHeader(
                    request.getHeader(jwtUtil.getTokenHeader())
            );
            // 2. 驗證 Token:非空 + 有效
            if (token != null) {
                // 2.1 從 Token 中獲取用戶名
                String username = jwtUtil.getUsernameFromToken(token);
                // 2.2 若用戶名存在,且 Spring Security 上下文未存儲用戶信息(未登錄)
                if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    // 2.3 從數(shù)據(jù)庫查詢用戶完整信息(UserDetails)
                    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                    // 2.4 驗證 Token 有效性(未過期 + 用戶名匹配)
                    if (jwtUtil.validateToken(token, userDetails)) {
                        // 3. 構(gòu)建認證對象,存入 Spring Security 上下文
                        UsernamePasswordAuthenticationToken authentication =
                                new UsernamePasswordAuthenticationToken(
                                        userDetails, // 用戶信息
                                        null, // 密碼(已驗證,無需存儲)
                                        userDetails.getAuthorities() // 用戶權(quán)限(角色)
                                );
                        // 設(shè)置請求詳情(如 IP、會話 ID)
                        authentication.setDetails(
                                new WebAuthenticationDetailsSource().buildDetails(request)
                        );
                        // 將認證對象存入上下文:后續(xù)接口權(quán)限校驗會從這里獲取用戶信息
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            }
        } catch (Exception e) {
            // Token 驗證失?。ㄈ邕^期、篡改),打印日志但不阻斷請求(后續(xù)會返回 401)
            logger.error("JWT Token 驗證失?。? + e.getMessage());
        }
        // 4. 繼續(xù)執(zhí)行過濾器鏈(讓請求到達后續(xù)接口或過濾器)
        filterChain.doFilter(request, response);
    }
}

七、第五步:重構(gòu) SecurityConfig(適配 JWT)

之前的 SecurityConfig 基于 HTTP Basic 登錄,現(xiàn)在需要修改為 JWT 無狀態(tài)登錄,核心調(diào)整點:

  • 關(guān)閉 Session(無狀態(tài)登錄不需要 Session);
  • 放行登錄接口(/api/login);
  • 將 JWT 過濾器加入過濾器鏈;
  • 移除 HTTP Basic 配置,保留統(tǒng)一異常處理。

修改 com.example.firstspringbootproject.config.SecurityConfig 類:

package com.example.firstspringbootproject.config;
import com.example.firstspringbootproject.common.Result;
import com.example.firstspringbootproject.entity.SysUser;
import com.example.firstspringbootproject.filter.JwtAuthenticationFilter;
import com.example.firstspringbootproject.mapper.SysUserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
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.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
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
@EnableMethodSecurity // 啟用方法級權(quán)限控制(如 @PreAuthorize("hasRole('ADMIN')"))
public class SecurityConfig {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter; // 自定義 JWT 過濾器
    // 1. 密碼編碼器(不變)
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    // 2. 用戶詳情服務(wù)(不變)
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            SysUser sysUser = sysUserMapper.findByUsername(username);
            if (sysUser == null) {
                throw new UsernameNotFoundException("用戶不存在: " + username);
            }
            return sysUser;
        };
    }
    // 3. 認證提供者(不變)
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService());
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }
    // 4. 認證管理器(不變)
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
    // 5. 核心規(guī)則配置(重點修改:適配 JWT)
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // 1. 關(guān)閉 CSRF(前后端分離必須關(guān))
                .csrf(csrf -> csrf.disable())
                // 2. 關(guān)閉 Session(無狀態(tài)登錄核心:不創(chuàng)建和使用 Session)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                // 3. 配置接口訪問規(guī)則(調(diào)整放行接口)
                .authorizeHttpRequests(auth -> auth
                        // ① 放行:接口文檔(Knife4j)、登錄接口
                        .requestMatchers("/doc.html", "/webjars/**", "/v3/api-docs/**", "/api/login").permitAll()
                        // ② 管理員接口:僅 ADMIN 可訪問
                        .requestMatchers("/api/user/all").hasRole("ADMIN")
                        // ③ 普通用戶接口:USER/ADMIN 可訪問
                        .requestMatchers("/api/user/**", "/api/product/**").hasAnyRole("USER", "ADMIN")
                        // ④ 其他接口:必須登錄(Token 有效)
                        .anyRequest().authenticated()
                )
                // 4. 加入 JWT 過濾器:在 UsernamePasswordAuthenticationFilter 之前執(zhí)行
                // (先驗證 Token,再執(zhí)行后續(xù)認證邏輯)
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                // 5. 統(tǒng)一異常處理(不變:未登錄返回 401,權(quán)限不足返回 403)
                .exceptionHandling(ex -> ex
                        .authenticationEntryPoint((request, response, authException) -> {
                            response.setContentType("application/json;charset=UTF-8");
                            Result<Void> result = Result.error(401, "未登錄或 Token 已過期,請重新登錄");
                            response.getWriter().write(objectMapper.writeValueAsString(result));
                        })
                        .accessDeniedHandler((request, response, accessDeniedException) -> {
                            response.setContentType("application/json;charset=UTF-8");
                            Result<Void> result = Result.error(403, "權(quán)限不足,無法訪問");
                            response.getWriter().write(objectMapper.writeValueAsString(result));
                        })
                );
        // 關(guān)聯(lián)認證提供者
        http.authenticationProvider(authenticationProvider());
        return http.build();
    }
}

八、第六步:測試 JWT 無狀態(tài)登錄流程

啟動項目,用 Apifox 測試完整流程,驗證無狀態(tài)登錄和權(quán)限控制是否生效:

1. 測試 1:調(diào)用登錄接口,獲取 Token

  • 請求地址http://localhost:8080/api/login
  • 請求方法:POST
  • 請求體(JSON)
{
    "username": "admin",
    "password": "123456"
}
  • 響應(yīng)結(jié)果(成功)
{
    "code": 200,
    "msg": "success",
    "data": {
        "role": "ROLE_ADMIN",
        "expiration": "Token 有效期 2 小時,請及時刷新",
        "token": "eyJhbGciOiJIUzUxMiJ9.eyJyb2xlcyI6WyJST0xFX0FETUlOIl0sInN1YiI6ImFkbWluIiwiaWF0IjoxNzYzNDgxMDk0LCJleHAiOjE3NjM0ODgyOTR9.hSxBCTlQo5tUai-knfM2Sh4nVmehizOslcHkvG86jdC-U7-EztskPkF0r4G0DcaTMV3eFcJUu4pCewDdnniq2A"
    }
}

復(fù)制返回的 token,后續(xù)請求會用到。

2. 測試 2:攜帶 Token 訪問管理員接口

  • 請求地址http://localhost:8080/api/user/all(需 ADMIN 角色)
  • 請求方法:GET
  • 請求頭:添加 Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJyb2xlcyI6WyJST0xFX0FETUlOIl0sInN1YiI6ImFkbWluIiwiaWF0IjoxNzYzNDgxMDk0LCJleHAiOjE3NjM0ODgyOTR9.hSxBCTlQo5tUai-knfM2Sh4nVmehizOslcHkvG86jdC-U7-EztskPkF0r4G0DcaTMV3eFcJUu4pCewDdnniq2A(注意 Bearer 后有空格)
  • 響應(yīng)結(jié)果(成功)
{
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "name": "小明",
            "age": 20,
            "phone": "13800138000"
        },
        {
            "id": 2,
            "name": "小紅",
            "age": 19,
            "phone": "13900139000"
        },
        {
            "id": 3,
            "name": "小李",
            "age": 22,
            "phone": "13700137000"
        }
    ]
}

3. 測試 3:普通用戶 Token 訪問管理員接口(權(quán)限不足)

  • 先用 zhangsan(密碼 123456)調(diào)用登錄接口,獲取普通用戶 Token;
  • 攜帶普通用戶 Token 訪問 http://localhost:8080/api/user/all;
  • 響應(yīng)結(jié)果(失敗)
{
    "code": 403,
    "msg": "權(quán)限不足,無法訪問",
    "data": null
}

4. 測試 4:Token 過期(模擬)

  • 修改 application.ymljwt.expiration1000(1 秒過期);
  • 重新登錄獲取 Token,1 秒后攜帶 Token 訪問接口;
  • 響應(yīng)結(jié)果(失?。?/strong>:
{
    "code": 401,
    "msg": "未登錄或 Token 已過期,請重新登錄",
    "data": null
}

九、常見問題與優(yōu)化建議

1. 問題:Token 被盜用怎么辦?

  • 原因:JWT Token 一旦生成,在過期前無法主動作廢;
  • 解決方案
  1. 縮短 Token 過期時間(如 30 分鐘),同時實現(xiàn) Token 刷新接口(用舊 Token 換取新 Token,避免頻繁登錄);
  2. 維護一個 黑名單(如 Redis),用戶登出或 Token 被盜時,將 Token 加入黑名單,驗證時先檢查是否在黑名單中。

2. 問題:密鑰(secret)泄露怎么辦?

  • 原因:密鑰是 JWT 安全的核心,泄露后攻擊者可偽造 Token;
  • 解決方案
  1. 生產(chǎn)環(huán)境中,密鑰通過 環(huán)境變量配置中心 注入,不寫入代碼或配置文件;
  2. 使用 非對稱加密算法(如 RS256),用私鑰生成 Token,公鑰驗證 Token,私鑰嚴格保密。

## 十、總結(jié):JWT 無狀態(tài)登錄的核心價值

通過本文的改造,我們實現(xiàn)了 Spring Boot + Spring Security + JWT 的無狀態(tài)登錄,核心優(yōu)勢總結(jié)如下:

  • 分布式友好:服務(wù)器無需存儲 Session,多臺服務(wù)器可直接共用 Token 驗證邏輯;
  • 前后端分離適配:通過請求頭攜帶 Token,無需依賴 Cookie,適配移動端、小程序等多端場景;
  • 安全性提升:Token 包含過期時間和加密簽名,避免身份信息被篡改;
  • 擴展性強:Token 可自定義存儲用戶角色、權(quán)限等信息,減少數(shù)據(jù)庫查詢次數(shù)。

至此,恭喜你已具備初級程序員開發(fā)水平,可以試著投簡歷找工作啦。

到此這篇關(guān)于Spring Boot基于 JWT 優(yōu)化 Spring Security 無狀態(tài)登錄實戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Spring Boot JWT Spring Security 無狀態(tài)登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter

    dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter

    這篇文章主要介紹了dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • SpringMVC執(zhí)行過程詳細講解

    SpringMVC執(zhí)行過程詳細講解

    MVC是一種軟件設(shè)計典范,用一種業(yè)務(wù)邏輯、數(shù)據(jù)、界面顯示分離的方法組織代碼,將業(yè)務(wù)邏輯聚集到一個組件里面,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業(yè)務(wù)邏輯,MVC分層有助于管理和架構(gòu)復(fù)雜的應(yīng)用程序
    2022-08-08
  • java源碼解析之String類的compareTo(String otherString)方法

    java源碼解析之String類的compareTo(String otherString)方法

    這篇文章主要給大家介紹了關(guān)于java源碼解析之String類的compareTo(String otherString)方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • Springboot Tomcat APR模式詳解和實踐記錄

    Springboot Tomcat APR模式詳解和實踐記錄

    這篇文章主要介紹了Springboot Tomcat APR模式詳解和實踐記錄,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-04-04
  • Java的Struts框架中登陸功能的實現(xiàn)和表單處理器的使用

    Java的Struts框架中登陸功能的實現(xiàn)和表單處理器的使用

    這篇文章主要介紹了Java的Struts框架中登陸功能的實現(xiàn)和表單處理器的使用,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下
    2015-12-12
  • 一文詳解Java Function的高級使用技巧

    一文詳解Java Function的高級使用技巧

    本文總結(jié)了10種企業(yè)級Java Function的編程技巧,包括柯里化、EnumMap+Supplier、ImmutableMap+BiConsumer等,通過函數(shù)式編程解決實際問題,提高代碼復(fù)用性和可維護性,需要的朋友可以參考下
    2026-03-03
  • Java三大特性-封裝知識小結(jié)

    Java三大特性-封裝知識小結(jié)

    所有的面向?qū)ο缶幊陶Z言的思路都是差不多的,而這三大特性,則是思路中的支柱點,接下來我就重點講解了一下java三大特性-封裝,感興趣的朋友跟隨腳本之家小編一起看看吧
    2018-03-03
  • Java生成與解析二維碼的完整指南

    Java生成與解析二維碼的完整指南

    二維碼是一種能夠?qū)⒋罅啃畔⒕幋a在小空間的編碼器,它是一種由黑白方塊構(gòu)成的圖片,可以將文本、鏈接、圖像等不同類型的信息編碼到其中,下面我們就來看看如何通過Java實現(xiàn)生成與解析二維碼吧
    2026-05-05
  • Java webSerivce的使用看完你就明白了

    Java webSerivce的使用看完你就明白了

    因為前段時間,需要使用到webService來調(diào)用公司的其他系統(tǒng)api接口,但是請求方式和我熟知的http請求不一樣,是基于soap協(xié)議來傳輸xml數(shù)據(jù)格式,請求的參數(shù)極其復(fù)雜,需要封裝多層xml數(shù)據(jù)格式,并且我不知道對方的api接口是什么語言,甚至不知道他們存在于什么平臺
    2022-03-03
  • Spring AI對接大模型開發(fā)易錯點總結(jié)與實戰(zhàn)解決辦法

    Spring AI對接大模型開發(fā)易錯點總結(jié)與實戰(zhàn)解決辦法

    本文介紹了SpringAI接入大模型時常見的問題及解決方案,主要從地址配置、密鑰鑒權(quán)、模型匹配、版本依賴四大維度入手,詳細分析了各種問題的成因,并提供了實用的配置與代碼解決方案,幫助開發(fā)者快速避坑,提高開發(fā)效率,需要的朋友可以參考下
    2026-05-05

最新評論

黔南| 海淀区| 凌源市| 古浪县| 宝兴县| 丰原市| 万源市| 迁安市| 花莲市| 新竹县| 浦东新区| 潼关县| 南京市| 大丰市| 宁城县| 安图县| 瓦房店市| 通河县| 都安| 曲阳县| 民乐县| 仪陇县| 六枝特区| 梁平县| 偏关县| 镇巴县| 陆丰市| 灵武市| 项城市| 三河市| 申扎县| 吴川市| 遂昌县| 丰宁| 湘乡市| 铜川市| 安顺市| 霍州市| 通江县| 丹棱县| 天祝|