" />

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

JWT + 攔截器實現(xiàn)無狀態(tài)登錄系統(tǒng)

 更新時間:2025年08月21日 11:10:47   作者:白侖色  
JWT(JSON Web Token)提供了一種無狀態(tài)的解決方案:用戶登錄后,服務(wù)器返回一個 Token,后續(xù)請求攜帶該 Token 即可完成身份驗證,無需服務(wù)器存儲會話信息,本文將結(jié)合 Spring Boot 攔截器,手把手實現(xiàn)一個完整的JWT無狀態(tài)登錄系統(tǒng),感興趣的朋友一起看看吧

? 引言

在現(xiàn)代 Web 開發(fā)中,傳統(tǒng)的 Session 認證方式在分布式、微服務(wù)架構(gòu)下面臨挑戰(zhàn):

  • Session 存儲依賴服務(wù)器內(nèi)存或 Redis
  • 跨域、跨服務(wù)共享 Session 復(fù)雜
  • 增加服務(wù)器負擔

JWT(JSON Web Token) 提供了一種無狀態(tài)的解決方案:用戶登錄后,服務(wù)器返回一個 Token,后續(xù)請求攜帶該 Token 即可完成身份驗證,無需服務(wù)器存儲會話信息。

本文將結(jié)合 Spring Boot 攔截器,手把手實現(xiàn)一個完整的 JWT 無狀態(tài)登錄系統(tǒng)。

?? 一、JWT 是什么?

JWT 是一個開放標準(RFC 7519),用于在各方之間安全地傳輸信息。

一個 JWT 通常由三部分組成,用 . 分隔:

xxxxx.yyyyy.zzzzz
  • Header:令牌類型和簽名算法
  • Payload:存放用戶信息(如用戶 ID、角色、過期時間等)
  • Signature:簽名,用于驗證 Token 是否被篡改

? 優(yōu)點:自包含、可擴展、跨語言、無狀態(tài)。

?? 二、技術(shù)選型

  • Spring Boot 2.7+
  • JWT 庫io.jsonwebtoken:jjwt
  • 加密算法:HMAC SHA256
  • 攔截器HandlerInterceptor

?? 三、項目結(jié)構(gòu)

src/
├── main/
│   ├── java/
│   │   └── com/example/jwtlogin/
│   │       ├── config/            → 配置類
│   │       ├── interceptor/       → 攔截器
│   │       ├── util/              → 工具類
│   │       ├── controller/        → 控制器
│   │       └── entity/            → 實體類

?? 四、核心代碼實現(xiàn)

4.1 添加依賴(pom.xml)

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

?? 注意:JJWT 0.11+ 版本拆分了模塊,需引入三個依賴。

4.2 JWT 工具類

@Component
public class JwtUtil {
    // 密鑰(應(yīng)放在配置文件中)
    private static final String SECRET = "your-256-bit-secret-your-256-bit-secret";
    // 過期時間:24小時
    private static final long EXPIRATION = 1000 * 60 * 60 * 24;
    /**
     * 生成 Token
     */
    public String generateToken(String username, Long userId, String role) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("role", role);
        return Jwts.builder()
                .setSubject(username)
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, SECRET)
                .compact();
    }
    /**
     * 解析 Token
     */
    public Claims parseToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            throw new RuntimeException("Token 已過期");
        } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
            throw new RuntimeException("Token 無效");
        }
    }
    /**
     * 驗證 Token 是否有效
     */
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

4.3 登錄控制器

@RestController
@RequestMapping("/auth")
public class AuthController {
    @Autowired
    private JwtUtil jwtUtil;
    /**
     * 用戶登錄
     */
    @PostMapping("/login")
    public ResponseEntity<Map<String, Object>> login(@RequestBody LoginRequest request) {
        // 這里應(yīng)調(diào)用 UserService 驗證用戶名密碼
        // 為簡化,假設(shè)用戶名密碼正確
        if ("admin".equals(request.getUsername()) && "123456".equals(request.getPassword())) {
            String token = jwtUtil.generateToken(request.getUsername(), 1L, "ADMIN");
            Map<String, Object> result = new HashMap<>();
            result.put("token", token);
            result.put("username", request.getUsername());
            result.put("role", "ADMIN");
            return ResponseEntity.ok(result);
        } else {
            return ResponseEntity.status(401).body(Map.of("msg", "用戶名或密碼錯誤"));
        }
    }
    /**
     * 用戶登出(前端清空 Token 即可)
     */
    @PostMapping("/logout")
    public ResponseEntity<String> logout() {
        return ResponseEntity.ok("登出成功");
    }
}
// 登錄請求 DTO
class LoginRequest {
    private String username;
    private String password;
    // getter & setter
}

4.4 JWT 攔截器

@Component
public class JwtInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtUtil jwtUtil;
    private static final String AUTH_HEADER = "Authorization";
    private static final String TOKEN_PREFIX = "Bearer ";
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        // 1. 放行 OPTIONS 請求(預(yù)檢請求)
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        // 2. 放行登錄接口
        String requestURI = request.getRequestURI();
        if ("/auth/login".equals(requestURI) || "/auth/logout".equals(requestURI)) {
            return true;
        }
        // 3. 獲取并驗證 Token
        String token = request.getHeader(AUTH_HEADER);
        if (token == null || !token.startsWith(TOKEN_PREFIX)) {
            response.setStatus(401);
            response.getWriter().write("{\"code\":401,\"msg\":\"缺少 Token\"}");
            return false;
        }
        token = token.substring(TOKEN_PREFIX.length());
        try {
            Claims claims = jwtUtil.parseToken(token);
            // 將用戶信息存入 request,供后續(xù) Controller 使用
            request.setAttribute("currentUser", claims.getSubject());
            request.setAttribute("userId", claims.get("userId"));
            request.setAttribute("role", claims.get("role"));
            return true;
        } catch (RuntimeException e) {
            response.setStatus(401);
            response.getWriter().write("{\"code\":401,\"msg\":\"" + e.getMessage() + "\"}");
            return false;
        }
    }
}

4.5 注冊攔截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private JwtInterceptor jwtInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/api/**")        // 保護所有 API 接口
                .excludePathPatterns("/auth/**", "/public/**"); // 放行認證和公共接口
    }
}

4.6 測試受保護的接口

@RestController
@RequestMapping("/api")
public class UserController {
    @GetMapping("/profile")
    public Map<String, Object> getProfile(HttpServletRequest request) {
        Map<String, Object> profile = new HashMap<>();
        profile.put("username", request.getAttribute("currentUser"));
        profile.put("userId", request.getAttribute("userId"));
        profile.put("role", request.getAttribute("role"));
        profile.put("email", "admin@example.com");
        return profile;
    }
    @GetMapping("/admin/data")
    public String adminData(HttpServletRequest request) {
        String role = (String) request.getAttribute("role");
        if ("ADMIN".equals(role)) {
            return "敏感數(shù)據(jù):只有管理員可見";
        } else {
            return "權(quán)限不足";
        }
    }
}

?? 五、測試流程

1. 登錄獲取 Token

curl -X POST http://localhost:8080/auth/login \
     -H "Content-Type: application/json" \
     -d '{"username":"admin","password":"123456"}'

響應(yīng)

{
  "token": "eyJhbGciOiJIUzI1NiJ9.xxxxx.yyyyy",
  "username": "admin",
  "role": "ADMIN"
}

2. 攜帶 Token 訪問受保護接口

curl http://localhost:8080/api/profile \
     -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxx.yyyyy"

響應(yīng)

{
  "username": "admin",
  "userId": 1,
  "role": "ADMIN",
  "email": "admin@example.com"
}

3. 不帶 Token 訪問 → 401 未授權(quán)

?? 六、生產(chǎn)環(huán)境優(yōu)化建議

優(yōu)化點說明
?? 密鑰安全SECRET 放在 application.yml 或環(huán)境變量中,不要硬編碼
?? Token 刷新實現(xiàn) Refresh Token 機制,避免頻繁登錄
?? Token 黑名單使用 Redis 記錄已注銷的 Token,防止被盜用
?? 自定義注解使用 @RequireAuth(role="ADMIN") 簡化權(quán)限控制
?? 日志監(jiān)控記錄 Token 解析失敗日志,便于排查問題

? 總結(jié)

步驟說明
1. 用戶登錄驗證賬號密碼,生成 JWT
2. 客戶端存儲將 Token 存入 localStorage 或 Cookie
3. 攜帶請求每次請求在 Authorization 頭中攜帶 Bearer Token
4. 攔截器驗證解析 Token,校驗簽名和過期時間
5. 放行或拒絕驗證通過則放行,否則返回 401

?? 無狀態(tài)登錄的核心服務(wù)器不保存會話狀態(tài),所有信息都封裝在 Token 中,由客戶端負責攜帶和管理。

?? 推薦

到此這篇關(guān)于JWT + 攔截器實現(xiàn)無狀態(tài)登錄系統(tǒng)的文章就介紹到這了,更多相關(guān)JWT 攔截器無狀態(tài)登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Socket一對多通信實現(xiàn)之并發(fā)處理方式

    Java Socket一對多通信實現(xiàn)之并發(fā)處理方式

    這篇文章主要介紹了Java Socket一對多通信實現(xiàn)之并發(fā)處理方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • java web在高并發(fā)和分布式下實現(xiàn)訂單號生成唯一的解決方案

    java web在高并發(fā)和分布式下實現(xiàn)訂單號生成唯一的解決方案

    這篇文章主要介紹了java web在高并發(fā)和分布式下實現(xiàn)訂單號生成唯一的解決方案,需要的朋友可以參考下
    2017-11-11
  • Java基礎(chǔ)學(xué)習(xí)之字符串知識總結(jié)

    Java基礎(chǔ)學(xué)習(xí)之字符串知識總結(jié)

    今天帶著大家復(fù)習(xí)一下Java基礎(chǔ)知識-字符串,文中介紹的非常詳細,對初步學(xué)習(xí)Java或者復(fù)習(xí)Java的小伙伴們都很有幫助喲,需要的朋友可以參考下
    2021-05-05
  • RocketMQ事務(wù)消息保證消息的可靠性和一致性

    RocketMQ事務(wù)消息保證消息的可靠性和一致性

    RocketMQ事務(wù)消息是一種能夠保證消息傳遞的可靠性和一致性的消息傳遞模式。它通過引入“半消息”和“事務(wù)狀態(tài)”機制,實現(xiàn)了消息發(fā)送和本地事務(wù)執(zhí)行的原子性,從而確保了消息的可靠性和一致性
    2023-04-04
  • springboot IDEA啟動兩個端口服務(wù)nginx負載過程

    springboot IDEA啟動兩個端口服務(wù)nginx負載過程

    這篇文章主要介紹了springboot IDEA啟動兩個端口服務(wù)nginx負載過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • springboot?使用clickhouse實時大數(shù)據(jù)分析引擎(使用方式)

    springboot?使用clickhouse實時大數(shù)據(jù)分析引擎(使用方式)

    這篇文章主要介紹了springboot?使用clickhouse實時大數(shù)據(jù)分析引擎的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2022-02-02
  • 使用 Redis 緩存實現(xiàn)點贊和取消點贊的示例代碼

    使用 Redis 緩存實現(xiàn)點贊和取消點贊的示例代碼

    這篇文章主要介紹了使用 Redis 緩存實現(xiàn)點贊和取消點贊的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Spring?cache源碼深度解析

    Spring?cache源碼深度解析

    緩存用于提升系統(tǒng)的性能,特別適用于一些對資源需求比較高的操作,下面這篇文章主要給大家介紹了關(guān)于Spring?cache源碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-03-03
  • SpringBoot結(jié)合Redis實現(xiàn)緩存

    SpringBoot結(jié)合Redis實現(xiàn)緩存

    本文主要介紹了SpringBoot結(jié)合Redis實現(xiàn)緩存,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Java NIO寫大文件對比(win7和mac)

    Java NIO寫大文件對比(win7和mac)

    這篇文章主要介紹了Java NIO寫大文件對比(win7和mac),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07

最新評論

红桥区| 安乡县| 建始县| 兴义市| 赣榆县| 保康县| 钟祥市| 阿坝县| 高淳县| 商水县| 景泰县| 博野县| 六枝特区| 交城县| 襄樊市| 鹤峰县| 禹州市| 彩票| 濮阳县| 朝阳区| 盐池县| 柞水县| 龙胜| 绥阳县| 凭祥市| 广昌县| 长春市| 永登县| 玉环县| 迭部县| 庆阳市| 通渭县| 淮南市| 尤溪县| 昂仁县| 上林县| 乌拉特中旗| 铁岭市| 监利县| 柳河县| 隆回县|