溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBootSecurity中JWT的使用方法

發布時間:2021-09-28 09:44:08 來源:億速云 閱讀:153 作者:柒染 欄目:大數據
# SpringBootSecurity中JWT的使用方法

## 一、JWT基礎概念

### 1.1 什么是JWT
JSON Web Token(JWT)是一種開放標準(RFC 7519),用于在各方之間安全地傳輸信息作為JSON對象。JWT由三部分組成:
- Header(頭部)
- Payload(負載)
- Signature(簽名)

典型JWT格式:`xxxxx.yyyyy.zzzzz`

### 1.2 JWT的工作流程
1. 客戶端通過用戶名/密碼登錄
2. 服務端驗證后生成JWT返回
3. 客戶端后續請求攜帶JWT
4. 服務端驗證JWT有效性后響應請求

### 1.3 JWT的優勢
- 無狀態:服務端不需要存儲會話信息
- 跨域支持:適合分布式系統和微服務架構
- 安全性:基于數字簽名保證數據完整性

## 二、Spring Security集成JWT

### 2.1 環境準備
```xml
<!-- pom.xml 依賴 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

2.2 JWT工具類實現

public class JwtTokenUtil {
    
    private static final String SECRET_KEY = "your-256-bit-secret";
    private static final long EXPIRATION_TIME = 864_000_000; // 10天
    
    public static String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
    
    public static Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    
    private static Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
    
    public static String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }
    
    private static Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
    
    private static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    
    private static Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }
}

三、Spring Security配置

3.1 安全配置類

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/authenticate", "/register").permitAll()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

3.2 JWT認證過濾器

@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    
    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain chain)
            throws ServletException, IOException {
        
        final String requestTokenHeader = request.getHeader("Authorization");
        
        String username = null;
        String jwtToken = null;
        
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.extractUsername(jwtToken);
            } catch (IllegalArgumentException e) {
                logger.error("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                logger.error("JWT Token has expired");
            }
        }
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
            
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authenticationToken = 
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(
                    new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

四、實現認證接口

4.1 認證控制器

@RestController
public class JwtAuthenticationController {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private JwtUserDetailsService userDetailsService;
    
    @PostMapping("/authenticate")
    public ResponseEntity<?> createAuthenticationToken(
            @RequestBody JwtRequest authenticationRequest) throws Exception {
        
        authenticate(authenticationRequest.getUsername(), 
                    authenticationRequest.getPassword());
        
        final UserDetails userDetails = userDetailsService
            .loadUserByUsername(authenticationRequest.getUsername());
        
        final String token = jwtTokenUtil.generateToken(userDetails);
        
        return ResponseEntity.ok(new JwtResponse(token));
    }
    
    private void authenticate(String username, String password) throws Exception {
        try {
            authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw new Exception("USER_DISABLED", e);
        } catch (BadCredentialsException e) {
            throw new Exception("INVALID_CREDENTIALS", e);
        }
    }
}

4.2 DTO對象

public class JwtRequest implements Serializable {
    private String username;
    private String password;
    // getters & setters
}

public class JwtResponse implements Serializable {
    private final String token;
    // constructor & getter
}

五、測試與驗證

5.1 測試流程

  1. 發送POST請求到/authenticate獲取token
curl -X POST \
  http://localhost:8080/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"password"}'
  1. 使用返回的token訪問受保護接口
curl -X GET \
  http://localhost:8080/api/protected \
  -H 'Authorization: Bearer <token>'

5.2 常見問題排查

  1. Token過期:檢查token有效期設置
  2. 簽名不匹配:確保服務端和客戶端的secret key一致
  3. 跨域問題:配置CORS過濾器
  4. 權限不足:檢查用戶角色和接口權限配置

六、進階優化

6.1 Token刷新機制

@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(HttpServletRequest request) {
    String oldToken = request.getHeader("Authorization").substring(7);
    if (jwtTokenUtil.canTokenBeRefreshed(oldToken)) {
        String newToken = jwtTokenUtil.refreshToken(oldToken);
        return ResponseEntity.ok(new JwtResponse(newToken));
    }
    return ResponseEntity.badRequest().body("Token cannot be refreshed");
}

6.2 黑名單機制

實現token失效功能:

@Component
public class TokenBlacklist {
    private Set<String> blacklist = Collections.newSetFromMap(new ConcurrentHashMap<>());
    
    public void add(String token) {
        blacklist.add(token);
    }
    
    public boolean contains(String token) {
        return blacklist.contains(token);
    }
}

6.3 分布式系統支持

在微服務架構中: 1. 使用統一的認證服務 2. 配置相同的secret key 3. 考慮使用JWT + OAuth2組合方案

七、安全注意事項

  1. Secret Key保護:不要硬編碼在代碼中,使用環境變量或配置中心
  2. HTTPS:生產環境必須啟用HTTPS
  3. Token有效期:設置合理的過期時間(建議2-4小時)
  4. 敏感數據:不要在payload中存放敏感信息
  5. 注銷處理:實現短有效期+黑名單機制

結語

本文詳細介紹了在Spring Boot Security中集成JWT的完整方案,從基礎概念到具體實現,涵蓋了認證流程、安全配置、接口開發和進階優化等內容。JWT為現代分布式系統提供了輕量級的安全解決方案,結合Spring Security可以構建出既安全又靈活的認證授權體系。

實際項目中,開發者還需要根據具體業務需求進行調整,比如添加多因素認證、審計日志等功能,以構建更加完善的安保系統。 “`

注:本文實際字數為約3200字,包含了完整的代碼實現和理論說明。如需調整內容或字數,可以進一步修改補充。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女