精品一区二区三区在线成人,欧美精产国品一二三区,Ji大巴进入女人66h,亚洲春色在线视频

Serverless 安全增強篇:整合 OAuth2 + Token 黑名單 + Redis 緩存機制

數據庫 Redis
隨著 Serverless 架構的廣泛應用,傳統 Web 應用中依賴 Session 的認證模式面臨重大挑戰。本文將以 Spring Security 為核心,構建一套無狀態、可擴展、支持 OAuth2、JWT、Redis 黑名單與 Refresh Token 的安全認證體系,適用于 Serverless 應用場景。

隨著 Serverless 架構的廣泛應用,傳統 Web 應用中依賴 Session 的認證模式面臨重大挑戰。本文將以 Spring Security 為核心,構建一套無狀態、可擴展、支持 OAuth2、JWT、Redis 黑名單與 Refresh Token 的安全認證體系,適用于 Serverless 應用場景。

Serverless 應用的安全挑戰

Serverless 應用的無狀態特性決定了其認證模型不能依賴傳統的會話管理。核心挑戰包括:

  • 身份認證用戶身份需要跨請求驗證,無 Session。
  • 權限校驗如何高效識別用戶角色與權限。
  • Token 生命周期管理訪問令牌的過期續簽、注銷。
  • 密鑰管理JWT 簽名密鑰的安全管理。

Spring Security + JWT 構建輕量認證模型

我們采用如下組件構建無狀態認證體系:

組件

作用

JWT

用戶身份令牌,無狀態傳遞

OAuth2

多客戶端支持與統一授權

Redis

黑名單 + RefreshToken 存儲

Spring Security

安全攔截器與權限控制

系統結構設計圖

+-----------------------------+
|        前端調用接口         |
+-----------------------------+
             |
             v
+-----------------------------+
|   API網關 / Serverless函數   |
+-----------------------------+
             |
             v
+-----------------------------+
|  Spring Security + Token攔截 |
+-----------------------------+
             |
             v
+------------+   Redis   +-------------+
| JWT Token校驗 | <----> | Token黑名單 |
+------------+           +-------------+
             |
             v
    +--------------------------+
    |     用戶業務邏輯處理      |
    +--------------------------+

關鍵模塊代碼實現

Spring Boot 啟動類

@SpringBootApplication
public class ServerlessSecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerlessSecurityApplication.class, args);
    }
}

Security 配置類(無狀態、JWT、資源服務器)

@Configuration
@EnableWebSecurity
public class SecurityConfig {


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(\"/api/public/**\", \"/api/token/refresh\").permitAll()
                        .anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
                .build();
    }
}

JWT 工具類

public class JwtUtils {


    private static final Key key = Keys.hmacShaKeyFor(\"0123456789abcdef0123456789abcdef\".getBytes());


    public static String generateAccessToken(String username, String roles, String jti) {
        return Jwts.builder()
                .setSubject(username)
                .setId(jti)
                .claim(\"roles\", roles)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 3600_000)) // 1小時
                .signWith(key)
                .compact();
    }


    public static String generateRefreshToken(String username, String jti) {
        return Jwts.builder()
                .setSubject(username)
                .setId(jti)
                .claim(\"type\", \"refresh\")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 3600_000)) // 7天
                .signWith(key)
                .compact();
    }


    public static Claims getClaims(String token) throws JwtException {
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
}

Redis Token 黑名單工具類

@Component
public class TokenBlacklistUtil {


    private final StringRedisTemplate redisTemplate;


    public TokenBlacklistUtil(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }


    public void blacklistToken(String jti, long ttlSeconds) {
        redisTemplate.opsForValue().set(\"blacklist:\" + jti, \"1\", ttlSeconds, TimeUnit.SECONDS);
    }


    public boolean isTokenBlacklisted(String jti) {
        return redisTemplate.hasKey(\"blacklist:\" + jti);
    }
}

RefreshToken 存儲與刷新接口

@RestController
@RequestMapping(\"/api/token\")
public class TokenController {


    @Autowired
    private StringRedisTemplate redisTemplate;


    @PostMapping(\"/refresh\")
    public ResponseEntity<?> refresh(@RequestParam String refreshToken) {
        Claims claims = JwtUtils.getClaims(refreshToken);
        String jti = claims.getId();
        String username = claims.getSubject();


        // 校驗類型與黑名單
        if (!\"refresh\".equals(claims.get(\"type\"))) {
            return ResponseEntity.badRequest().body(\"非法 token 類型\");
        }


        if (!Boolean.TRUE.equals(redisTemplate.hasKey(\"refresh:\" + jti))) {
            return ResponseEntity.status(401).body(\"refreshToken 已失效\");
        }


        // 生成新 token
        String newJti = UUID.randomUUID().toString();
        String newAccessToken = JwtUtils.generateAccessToken(username, \"USER\", newJti);


        return ResponseEntity.ok(Map.of(\"accessToken\", newAccessToken));
    }
}

登錄、退出控制器(模擬)

@RestController
@RequestMapping(\"/api/auth\")
public class AuthController {


    @Autowired
    private StringRedisTemplate redisTemplate;


    @Autowired
    private TokenBlacklistUtil blacklistUtil;


    @PostMapping(\"/login\")
    public Map<String, String> login(@RequestParam String username) {
        String jti = UUID.randomUUID().toString();
        String accessToken = JwtUtils.generateAccessToken(username, \"USER\", jti);
        String refreshToken = JwtUtils.generateRefreshToken(username, jti);


        // 保存 refreshToken 到 redis
        redisTemplate.opsForValue().set(\"refresh:\" + jti, username, 7, TimeUnit.DAYS);


        return Map.of(\"accessToken\", accessToken, \"refreshToken\", refreshToken);
    }


    @PostMapping(\"/logout\")
    public ResponseEntity<Void> logout(@RequestHeader(\"Authorization\") String authHeader) {
        String token = authHeader.replace(\"Bearer \", \"\");
        Claims claims = JwtUtils.getClaims(token);
        String jti = claims.getId();
        long remaining = (claims.getExpiration().getTime() - System.currentTimeMillis()) / 1000;
        blacklistUtil.blacklistToken(jti, remaining);


        // 同時清除 refreshToken
        redisTemplate.delete(\"refresh:\" + jti);
        return ResponseEntity.ok().build();
    }
}

application.yml 配置

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.icoderoad.com/oauth2
          jwk-set-uri: https://auth.icoderoad.com/oauth2/jwks
  redis:
    host: localhost
    port: 6379

總結

無狀態 Serverless 環境下,Spring Security 可通過 JWT、OAuth2 與 Redis 輕松實現高效認證體系:

  • 不依賴 Session
  • 支持訪問控制 + 黑名單管理
  • Refresh Token 保證登錄體驗
  • Redis 做 Token 生命周期緩存

今天就講到這里,如果有問題需要咨詢,大家可以直接留言或掃下方二維碼來知識星球找我,我們會盡力為你解答。

責任編輯:武曉燕 來源: 路條編程
相關推薦

2011-06-02 10:52:11

Android BroadCast 黑名單

2011-01-21 17:53:44

Zimbra

2023-08-31 08:34:07

Users對象序列化

2023-08-29 08:00:38

2013-08-27 10:56:24

2015-06-04 11:11:15

2018-03-12 10:45:41

2021-08-02 12:50:45

sessiontokenJava

2022-04-11 07:34:46

OAuth2UAA節點

2010-11-11 13:20:41

2010-05-24 13:36:11

2011-03-18 13:14:01

2011-07-28 11:10:58

2009-10-29 08:39:14

Windows 7系統激活

2018-06-10 09:04:28

2019-07-29 08:41:33

算法黑名單ip

2020-07-15 20:32:45

fail2banFirewallD系統運維

2012-11-23 10:15:06

2020-01-10 15:42:13

SpringBootRedis數據庫

2014-06-06 09:38:22

工信部應用軟件黑名單
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 葵青区| 舞钢市| 嘉义市| 陆河县| 谷城县| 清徐县| 攀枝花市| 海宁市| 松滋市| 葫芦岛市| 韶山市| 特克斯县| 平远县| 石台县| 历史| 新源县| 阿尔山市| 牡丹江市| 铜鼓县| 宁晋县| 银川市| 雅安市| 弋阳县| 鲁甸县| 凤阳县| 山阴县| 治县。| 呼玛县| 青冈县| 江永县| 望奎县| 嘉善县| 兴国县| 云南省| 贵港市| 郯城县| 奉化市| 安吉县| 惠安县| 隆化县| 砚山县|