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

Spring?Boot?JWT登錄授權(quán)使用(無感刷新)

 更新時(shí)間:2026年04月07日 11:47:19   作者:程序員越  
JWT作為一種輕量級(jí)的身份認(rèn)證與授權(quán)方案,憑借其無狀態(tài)、可跨域、易于擴(kuò)展的特性,成為?Spring?Boot?項(xiàng)目中實(shí)現(xiàn)認(rèn)證授權(quán)的主流選擇,本文詳解Spring?Boot整合?JWT?實(shí)現(xiàn)登錄認(rèn)證與授權(quán)的全流程,助力開發(fā)者快速搭建安全可靠的認(rèn)證授權(quán)體系,感興趣的可以了解一下

一、引言

在分布式系統(tǒng)和前后端分離架構(gòu)中,傳統(tǒng)的基于 Session 的認(rèn)證方式存在跨域難處理、服務(wù)端存儲(chǔ)壓力大等問題。JWT(JSON Web Token) 作為一種輕量級(jí)的身份認(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:用于編寫接口測(cè)試認(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:存儲(chǔ)算法類型和令牌類型,如{"alg":"HS256","typ":"JWT"}
  • Payload:存儲(chǔ)用戶身份信息(如用戶名、角色)和過期時(shí)間等聲明,不建議存放敏感信息
  • Signature:通過 Header 指定的算法,結(jié)合密鑰對(duì) Header 和 Payload 進(jìn)行加密,保證令牌不被篡改

認(rèn)證流程: 用戶登錄成功后,服務(wù)端生成 JWT 返回給客戶端;客戶端后續(xù)請(qǐng)求攜帶 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;
        }
        // 從請(qǐng)求頭獲取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分鐘時(shí),自動(dòng)刷新
    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;
        }
        // 獲取請(qǐng)求頭中的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);
            // 存儲(chǔ)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中存儲(chǔ)的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é)

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

到此這篇關(guān)于Spring Boot JWT登錄授權(quán)使用(無感刷新))的文章就介紹到這了,更多相關(guān)Spring Boot JWT登錄授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

军事| 横山县| 扶沟县| 大同市| 广宗县| 辽中县| 合阳县| 安吉县| 馆陶县| 鄂州市| 高雄市| 商城县| 哈密市| 静海县| 县级市| 启东市| 加查县| 阜康市| 灌云县| 瓮安县| 吉隆县| 荔波县| 延川县| 武威市| 东辽县| 秦皇岛市| 宜昌市| 武穴市| 永新县| 建昌县| 山东| 望江县| 鹤壁市| 霍林郭勒市| 鹤庆县| 共和县| 凤山市| 托克逊县| 当雄县| 望奎县| 会东县|