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

SpringBoot整合JWT實(shí)現(xiàn)登錄認(rèn)證與接口授權(quán)的全流程

 更新時間:2025年12月15日 09:59:26   作者:閑煮光陰  
JWT作為一種輕量級的身份認(rèn)證與授權(quán)方案,憑借其無狀態(tài)、可跨域、易于擴(kuò)展的特性,成為?Spring?Boot?項(xiàng)目中實(shí)現(xiàn)認(rèn)證授權(quán)的主流選擇,本文將從環(huán)境搭建、核心實(shí)現(xiàn)到進(jìn)階優(yōu)化,完整講解?Spring?Boot?整合?JWT?實(shí)現(xiàn)登錄認(rèn)證與接口授權(quán)的全流程,需要的朋友可以參考下

一、引言

在分布式系統(tǒng)和前后端分離架構(gòu)中,傳統(tǒng)的基于 Session 的認(rèn)證方式存在跨域難處理、服務(wù)端存儲壓力大等問題。JWT(JSON Web Token) 作為一種輕量級的身份認(rèn)證與授權(quán)方案,憑借其無狀態(tài)、可跨域、易于擴(kuò)展的特性,成為 Spring Boot 項(xiàng)目中實(shí)現(xiàn)認(rèn)證授權(quán)的主流選擇。本文將從環(huán)境搭建、核心實(shí)現(xiàn)到進(jìn)階優(yōu)化,完整講解 Spring Boot 整合 JWT 實(shí)現(xiàn)登錄認(rèn)證與接口授權(quán)的全流程。

二、技術(shù)棧與環(huán)境準(zhǔn)備

1. 核心依賴

在 Spring Boot 項(xiàng)目的pom.xml(Maven)或build.gradle(Gradle)中引入以下

  • Spring Security:提供認(rèn)證與授權(quán)的基礎(chǔ)框架
  • JJWT:Java 領(lǐng)域主流的 JWT 工具庫,支持 JWT 的生成、解析與驗(yàn)證
  • Spring Web:用于編寫接口測試認(rèn)證授權(quán)流程

Maven 依賴配置示例:

<!-- Spring Boot Starter Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- JWT -->
<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>

2. 核心概念說明

JWT 結(jié)構(gòu): 由 Header(頭部)、Payload(載荷)、Signature(簽名)三部分組成,以.分隔,例如eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

  • Header:存儲算法類型和令牌類型,如{"alg":"HS256","typ":"JWT"}
  • Payload:存儲用戶身份信息(如用戶名、角色)和過期時間等聲明,不建議存放敏感信息
  • Signature:通過 Header 指定的算法,結(jié)合密鑰對 Header 和 Payload 進(jìn)行加密,保證令牌不被篡改

認(rèn)證流程: 用戶登錄成功后,服務(wù)端生成 JWT 返回給客戶端;客戶端后續(xù)請求攜帶 JWT,服務(wù)端驗(yàn)證令牌有效性后完成授權(quán)

三、核心功能實(shí)現(xiàn)

1. JWT 工具類封裝

@Component
public class JwtUtils {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.access-token-expire-time}")
    private long accessTokenExpireTime;

    @Value("${jwt.refresh-token-expire-time}")
    private long refreshTokenExpireTime;

    /**
     * 生成訪問令牌
     */
    public String generateAccessToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return generateToken(claims, username, accessTokenExpireTime);
    }

    /**
     * 生成刷新令牌
     */
    public String generateRefreshToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return generateToken(claims, username, refreshTokenExpireTime);
    }

    /**
     * 生成token
     */
    private String generateToken(Map<String, Object> claims, String subject, long expireTime) {
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
        
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expireTime))
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();
    }

    /**
     * 從token中獲取用戶名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getSubject();
    }

    /**
     * 驗(yàn)證token是否有效
     */
    public boolean validateToken(String token) {
        try {
            SecretKey key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
            Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 判斷token是否過期
     */
    public boolean isTokenExpired(String token) {
        Claims claims = getClaimsFromToken(token);
        Date expiration = claims.getExpiration();
        return expiration.before(new Date());
    }

    /**
     * 從token中獲取claims
     */
    private Claims getClaimsFromToken(String token) {
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
}

2. 實(shí)現(xiàn)認(rèn)證過濾器

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 
            throws ServletException, IOException {
        
        // 跳過登錄和刷新令牌的接口
        String requestURI = request.getRequestURI();
        if (requestURI.equals("/api/auth/login") || requestURI.equals("/api/auth/refresh")) {
            chain.doFilter(request, response);
            return;
        }

        // 從請求頭獲取token
        String token = getTokenFromRequest(request);

        if (StringUtils.hasText(token) && jwtUtils.validateToken(token)) {
            String username = jwtUtils.getUsernameFromToken(token);
            
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            
            // 設(shè)置認(rèn)證信息到上下文
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } else {
            SecurityContextHolder.clearContext();
        }

        chain.doFilter(request, response);
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

3. 實(shí)現(xiàn)無感刷新令牌過濾器

創(chuàng)建JwtRefreshFilter實(shí)現(xiàn)令牌的無感刷新:

@Component
public class JwtRefreshFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserDetailsService userDetailsService;

    // 當(dāng)token剩余有效期小于10分鐘時,自動刷新
    private static final long REFRESH_THRESHOLD = 10 * 60 * 1000;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
            throws ServletException, IOException {
        
        // 跳過登錄和刷新接口
        String requestURI = request.getRequestURI();
        if (requestURI.equals("/api/auth/login") || requestURI.equals("/api/auth/refresh")) {
            filterChain.doFilter(request, response);
            return;
        }

        // 獲取請求頭中的token
        String authorizationHeader = request.getHeader("Authorization");
        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        String token = authorizationHeader.substring(7);
        try {
            if (jwtUtils.validateToken(token)) {
                // 檢查token是否即將過期
                long remainingTime = jwtUtils.getTokenRemainingTime(token);
                if (remainingTime < REFRESH_THRESHOLD) {
                    String username = jwtUtils.getUsernameFromToken(token);
                    String newAccessToken = jwtUtils.generateAccessToken(username);
                    // 將新token添加到響應(yīng)頭
                    response.setHeader("Authorization", "Bearer " + newAccessToken);
                }
            }
        } catch (Exception e) {
            SecurityContextHolder.clearContext();
        }
        
        filterChain.doFilter(request, response);
    }
}

4. 配置 Spring Security

創(chuàng)建SecurityConfig配置安全規(guī)則:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthFilter, 
                                                  JwtRefreshFilter jwtRefreshFilter) throws Exception {
        http
            .cors().and().csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests()
            .antMatchers("/api/auth/**", "/api/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint((request, response, ex) -> {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setContentType("application/json");
                response.getWriter().write("{\"error\":\"Unauthorized\",\"message\":\"Authentication required\"}");
            });

        // 添加JWT過濾器
        http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(jwtRefreshFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

5. 實(shí)現(xiàn)認(rèn)證服務(wù)

創(chuàng)建AuthService接口和實(shí)現(xiàn)類處理登錄和刷新令牌業(yè)務(wù):

public interface AuthService {
    LoginResponseDTO login(LoginRequestDTO loginRequest);
    LoginResponseDTO refreshToken(RefreshTokenRequestDTO refreshTokenRequest);
}

@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${jwt.refresh-token-expire-time}")
    private long refreshTokenExpireTime;

    @Override
    public LoginResponseDTO login(LoginRequestDTO loginRequest) {
        // 驗(yàn)證輸入
        if (loginRequest == null || StringUtils.isEmpty(loginRequest.getUsername()) || 
            StringUtils.isEmpty(loginRequest.getPassword())) {
            throw new BadCredentialsException("Invalid login request");
        }
        
        try {
            // 執(zhí)行認(rèn)證
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    loginRequest.getUsername(), 
                    loginRequest.getPassword()
                )
            );
            
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 生成token
            String username = loginRequest.getUsername();
            String accessToken = jwtUtils.generateAccessToken(username);
            String refreshToken = jwtUtils.generateRefreshToken(username);
            
            // 存儲refresh token到Redis
            String refreshTokenKey = "refresh_token:" + username;
            stringRedisTemplate.opsForValue().set(
                refreshTokenKey, 
                refreshToken, 
                refreshTokenExpireTime, 
                TimeUnit.MILLISECONDS
            );
            
            // 構(gòu)建響應(yīng)
            LoginResponseDTO response = new LoginResponseDTO();
            response.setAccessToken(accessToken);
            response.setRefreshToken(refreshToken);
            response.setExpiresIn(refreshTokenExpireTime);
            
            return response;
        } catch (AuthenticationException e) {
            throw new BadCredentialsException("Invalid username or password");
        }
    }

    @Override
    public LoginResponseDTO refreshToken(RefreshTokenRequestDTO refreshTokenRequest) {
        // 驗(yàn)證輸入
        if (refreshTokenRequest == null || StringUtils.isEmpty(refreshTokenRequest.getRefreshToken())) {
            throw new BadCredentialsException("Invalid refresh token request");
        }
        
        String refreshToken = refreshTokenRequest.getRefreshToken();
        
        // 驗(yàn)證refresh token
        if (!jwtUtils.validateToken(refreshToken)) {
            throw new BadCredentialsException("Invalid refresh token");
        }
        
        String username = jwtUtils.getUsernameFromToken(refreshToken);
        if (StringUtils.isEmpty(username)) {
            throw new BadCredentialsException("Invalid refresh token");
        }
        
        // 驗(yàn)證Redis中存儲的refresh token
        String storedToken = stringRedisTemplate.opsForValue().get("refresh_token:" + username);
        if (storedToken == null || !storedToken.equals(refreshToken)) {
            throw new BadCredentialsException("Invalid refresh token");
        }
        
        // 生成新的token
        String newAccessToken = jwtUtils.generateAccessToken(username);
        String newRefreshToken = jwtUtils.generateRefreshToken(username);
        
        // 更新Redis中的refresh token
        stringRedisTemplate.opsForValue().set(
            "refresh_token:" + username, 
            newRefreshToken, 
            refreshTokenExpireTime, 
            TimeUnit.MILLISECONDS
        );
        
        LoginResponseDTO response = new LoginResponseDTO();
        response.setAccessToken(newAccessToken);
        response.setRefreshToken(newRefreshToken);
        response.setExpiresIn(refreshTokenExpireTime);

        return response;
    }
}

四、相關(guān)注解總結(jié)

注解名稱                          核心作用                                        使用場景                                                            關(guān)鍵注意事項(xiàng)                                                                            
@EnableWebSecurity開啟 Spring Security 的 Web 安全功能,加載安全過濾器鏈和相關(guān)配置標(biāo)注在自定義的 Spring Security 配置類上            必須搭配@Configuration使用,否則配置不生效
@EnableGlobalMethodSecurity開啟方法級權(quán)限控制,支持多種權(quán)限注解標(biāo)注在 Security 配置類上,需指定啟用的注解類型   常用屬性:prePostEnabled=true(啟用@PreAuthorize等)、securedEnabled=true(啟用@Secured
@PreAuthorize方法執(zhí)行前校驗(yàn)權(quán)限,支持 SpEL 表達(dá)式,可實(shí)現(xiàn)復(fù)雜權(quán)限判斷控制器接口方法、服務(wù)層方法的權(quán)限控制(細(xì)粒度權(quán)限)        依賴@EnableGlobalMethodSecurity(prePostEnabled=true),支持角色、權(quán)限、請求參數(shù)校驗(yàn)
@PostAuthorize方法執(zhí)行后校驗(yàn)權(quán)限,基于方法返回值判斷需根據(jù)返回結(jié)果控制權(quán)限的場景(極少使用,避免方法執(zhí)行產(chǎn)生副作用)   SpEL 表達(dá)式中用returnObject指代方法返回值                          
@Secured基于角色的粗粒度權(quán)限控制                        控制器或服務(wù)層方法的角色校驗(yàn)        需啟用securedEnabled=true,角色名稱必須以ROLE_為前綴
@RolesAllowedJSR-250 規(guī)范注解,基于角色的權(quán)限控制    控制器或服務(wù)層方法的多角色授權(quán) 需啟用jsr250Enabled=true,角色名稱可省略ROLE_前綴(框架自動補(bǔ)充)
@AuthenticationPrincipal   直接獲取當(dāng)前認(rèn)證用戶的UserDetails或自定義用戶信息控制器方法參數(shù)中,需獲取當(dāng)前登錄用戶信息時  無需手動從SecurityContextHolder獲取,直接注入即可 
@PreFilter方法執(zhí)行前,對集合類型參數(shù)進(jìn)行過濾,僅保留符合權(quán)限的元素  數(shù)據(jù)查詢前的參數(shù)過濾(數(shù)據(jù)級權(quán)限控制)        僅對集合類型參數(shù)生效,SpEL 表達(dá)式用filterObject指代集合元素                    
@PostFilter方法執(zhí)行后,對集合類型返回值進(jìn)行過濾,僅保留符合權(quán)限的元素數(shù)據(jù)返回后的結(jié)果過濾(數(shù)據(jù)級權(quán)限控制)        僅對集合類型返回值生效,依賴@EnableGlobalMethodSecurity啟用              

總結(jié)

本指南介紹了如何在 Spring Boot 應(yīng)用中實(shí)現(xiàn)基于 JWT 的認(rèn)證授權(quán)功能,包括:

  • JWT 令牌的生成與驗(yàn)證
  • 基于 Spring Security 的認(rèn)證過濾器
  • 令牌刷新機(jī)制與無感刷新實(shí)現(xiàn)
  • 完整的登錄與授權(quán)流程

通過這種方式,我們可以實(shí)現(xiàn)無狀態(tài)的認(rèn)證系統(tǒng),適合分布式應(yīng)用和前后端分離架構(gòu)。實(shí)際應(yīng)用中,還可以根據(jù)需要添加更多功能,如令牌撤銷、角色權(quán)限控制等。

以上就是SpringBoot整合JWT實(shí)現(xiàn)登錄認(rèn)證與接口授權(quán)的全流程的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot JWT登錄認(rèn)證與接口授權(quán)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaSE網(wǎng)絡(luò)原理之UDP和TCP原理詳解

    JavaSE網(wǎng)絡(luò)原理之UDP和TCP原理詳解

    TCP、UDP都是屬于運(yùn)輸層的協(xié)議,提供端到端的進(jìn)程之間的邏輯通信,而IP協(xié)議是提供主機(jī)間的邏輯通信,應(yīng)用層規(guī)定應(yīng)用進(jìn)程在通信時所遵循的協(xié)議,這篇文章主要介紹了JavaSE網(wǎng)絡(luò)原理之UDP和TCP原理的相關(guān)資料,需要的朋友可以參考下
    2026-01-01
  • java實(shí)現(xiàn)文件上傳下載功能

    java實(shí)現(xiàn)文件上傳下載功能

    這篇文章主要介紹了java實(shí)現(xiàn)文件上傳下載功能,上傳單個或多個文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java有序二叉樹的刪除節(jié)點(diǎn)方式

    java有序二叉樹的刪除節(jié)點(diǎn)方式

    文章描述了在二叉樹中刪除節(jié)點(diǎn)的三種情況及其對應(yīng)的操作步驟,通過遞歸找到節(jié)點(diǎn)及其父節(jié)點(diǎn),并根據(jù)節(jié)點(diǎn)的子樹情況(無子樹、單子樹、雙子樹)進(jìn)行相應(yīng)的刪除操作,文章還提供了一個測試類來驗(yàn)證刪除操作的正確性
    2024-12-12
  • 簡單了解Spring中常用工具類

    簡單了解Spring中常用工具類

    這篇文章主要介紹了簡單了解Spring中常用工具類,非常全面,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • MyBatis-Plus 批量保存的操作方法

    MyBatis-Plus 批量保存的操作方法

    在項(xiàng)目開發(fā)中,需要插入批量插入20多萬條數(shù)據(jù),通過日志觀察,發(fā)現(xiàn)在調(diào)用MyBatis-Plus中的saveBatch()方法性能非常的差,本篇文章主要分享一下saveBatch()的原理以及使用的注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • MyBatis-Plus 插件擴(kuò)展的實(shí)現(xiàn)

    MyBatis-Plus 插件擴(kuò)展的實(shí)現(xiàn)

    MyBatis-Plus通過插件擴(kuò)展機(jī)制增強(qiáng)功能,基于MyBatis Interceptor攔截器,包括分頁插件、邏輯刪除、SQL性能分析和樂觀鎖等,開發(fā)者可自定義插件以適應(yīng)特定需求,有效地增強(qiáng)SQL執(zhí)行過程的控制和優(yōu)化,同時注意插件使用的性能影響和執(zhí)行順序
    2024-09-09
  • springcloud?gateway實(shí)現(xiàn)簡易版灰度路由步驟詳解

    springcloud?gateway實(shí)現(xiàn)簡易版灰度路由步驟詳解

    這篇文章主要為大家介紹了springcloud?gateway實(shí)現(xiàn)簡易版灰度路由步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • 查看Spring容器中bean的五種方法小結(jié)

    查看Spring容器中bean的五種方法小結(jié)

    近期在寫Spring項(xiàng)目的時候,需要通過注解的形式去替代之前直接將Bean存放在Spring容器這種方式,以此來簡化對于Bean對象的操作,這篇文章主要給大家介紹了關(guān)于如何查看Spring容器中bean的五種方法,需要的朋友可以參考下
    2024-05-05
  • 簡單總結(jié)Java的反射機(jī)制的運(yùn)用

    簡單總結(jié)Java的反射機(jī)制的運(yùn)用

    這篇文章主要介紹了Java的反射機(jī)制的運(yùn)用,對一些常用的類作了一些說明,需要的朋友可以參考下
    2015-11-11
  • Springmvc攔截器執(zhí)行順序及各方法作用詳解

    Springmvc攔截器執(zhí)行順序及各方法作用詳解

    這篇文章主要介紹了Springmvc的攔截器執(zhí)行順序及各方法作用,下面講實(shí)現(xiàn)其接口的寫法,先看一下這個接口的三個方法,需要的朋友可以參考下
    2018-07-07

最新評論

阳原县| 庄浪县| 金华市| 闽侯县| 武安市| 富民县| 邹平县| 西乡县| 徐水县| 洛南县| 赫章县| 唐海县| 左云县| 云龙县| 江油市| 安仁县| 芦山县| 山东省| 万载县| 固安县| 资中县| 满洲里市| 苏州市| 沙坪坝区| 盐池县| 沧州市| 康乐县| 新巴尔虎右旗| 宜兰县| 海门市| 荆门市| 富阳市| 监利县| 开平市| 南部县| 延安市| 临沧市| 建德市| 闽清县| 通江县| 浑源县|