在 SpringBoot 項目中簡單實現 JWT 驗證

来源:https://www.cnblogs.com/loogn/archive/2023/02/07/17099338.html
-Advertisement-
Play Games

使用 SpringBoot 提供 api 的時候,我更喜歡使用 jwt 的方式來做驗證。網上有會多 Spring Security 整合 jwt 的,也有 Shiro 整合 jwt 的,感覺有點複雜。這裡分享一下自己在項目中的簡單實現。 依賴包 除了 SpringBoot 基本的依賴,需要一個生成 ...


使用 SpringBoot 提供 api 的時候,我更喜歡使用 jwt 的方式來做驗證。網上有會多 Spring Security 整合 jwt 的,也有 Shiro 整合 jwt 的,感覺有點複雜。這裡分享一下自己在項目中的簡單實現。

依賴包

除了 SpringBoot 基本的依賴,需要一個生成 jwt 和序列化的包。生成 jwt 的包依賴很多,因為我項目里使用了 hutool 這個包,就只用用它了。

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.9</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.9</version>
        </dependency>

jwt用戶模型

定義一個 Jwt 的 sub 欄位模型,存儲用戶:

import lombok.Data;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Data
public class JwtUser {
    /**
     * 用戶編號
     */
    private Integer id;
    /**
     * 用戶名
     */
    private String name;
    /**
     * 角色
     */
    private String role;

    /**
     * 獲取當前請求用戶
     * @return
     */
    public static JwtUser getCurrentUser() {

        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return (JwtUser) request.getAttribute("user");
    }
}

驗證註解

定義一個用於請求類和方法的註解

import java.lang.annotation.*;

@Inherited
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorize {

    /**
     * 是否匿名可以訪問
     * @return
     */
    boolean anonymous() default false;

    /**
     * 角色
     * @return
     */
    String[] roles() default {};
}

JWT 幫助類

用於生成 jwt 和 解析 JwtUser 對象。


import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.google.gson.Gson;
import com.mpyf.xapi.security.JwtUser;
import lombok.var;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JwtTokenUtils {

    public static final String SECRET = "your_secret";
    public static final String ISS = "com.your.cn";

    private static final int EXPIRATIONHOURS = 24; //過期時間24小時
    
    //創建token
    public static String createToken(JwtUser user) {
        return createToken(user, EXPIRATIONHOURS);
    }

    public static String createToken(JwtUser user, int hours) {
        String subJson = new Gson().toJson(user);
        JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes());

        JWT jwt = JWT.create().setSigner(jwtSigner);
        jwt
                .setJWTId(UUID.randomUUID().toString().replace("-", ""))
                .setSubject(subJson) //用戶信息
                .setIssuer(ISS)      //簽發者
                //.setAudience("受眾")
                //.setNotBefore(new Date())
                .setIssuedAt(new Date())
                .setExpiresAt(new Date(System.currentTimeMillis() + hours * 3600 * 1000));
        return jwt.sign();
    }

    public static JwtUser getUser(String token) {

        if (StringHelper.isNullOrEmpty(token)) return null;

        var jwt = JWTUtil.parseToken(token);
        JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes());
        jwt.setSigner(jwtSigner);

        if (jwt.validate(10)) {
            var subJson = jwt.getPayload("sub").toString();
            JwtUser user = new Gson().fromJson(subJson, JwtUser.class);
            return user;
        } else {
            return null;
        }
    }
}

驗證攔截器

定義jwt的驗證攔截器,從請求頭獲取 token 解析並驗證。

import com.mpyf.xapi.helper.JwtTokenUtils;
import com.mpyf.xapi.helper.StringHelper;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

/**
 * jwt 驗證攔截器
 */
@Component
public class JwtAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //Authorization:Bearer+空格+token
        String token = request.getHeader("Authorization");
        if (token != null) {
            token = token.replace("Bearer ", "");
        }
        //處理模擬登錄的jwt
        if (StringHelper.isNullOrEmpty(token)) {
            token = request.getParameter("jwt");
        }
        if (StringHelper.isNullOrEmpty(token)) {
            //相容從請求參數傳token
            Object jwt = request.getAttribute("jwt");
            if (jwt != null) {
                token = jwt.toString();
            }
        }
        JwtUser user = JwtTokenUtils.getUser(token);
        request.setAttribute("user", user);

        if (handler instanceof HandlerMethod) {
            HandlerMethod h = (HandlerMethod) handler;
            Authorize authorize = h.getMethodAnnotation(Authorize.class);
            if (authorize == null) {
                authorize = h.getMethod().getDeclaringClass().getAnnotation(Authorize.class);
            }

            //如果沒有Authorize或者可以匿名訪問,直接返回
            if (authorize != null && !authorize.anonymous()) {
                {
                    if (user == null) {
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                        return false;
                    } else if (authorize.roles() != null && authorize.roles().length > 0 &&
                            Arrays.stream(authorize.roles()).allMatch(s -> !s.equalsIgnoreCase(user.getRole()))) {
                        //沒許可權
                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
                        return false;
                    }
                }
            }
        }
        return true;
    }
}

註冊攔截器

在 WebMvc 配置中註冊攔截器,並支持跨域請求

import com.mpyf.xapi.security.JwtAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    JwtAuthInterceptor jwtAuthInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtAuthInterceptor).addPathPatterns("/api/**");
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
                .allowedOriginPatterns("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                //.maxAge(3600)
                .allowCredentials(true);
        WebMvcConfigurer.super.addCorsMappings(registry);
    }
}

Controller中使用

@RestController
@RequestMapping("/api/test")
@Authorize(roles = {"admin", "user"})
public class TestController {

    @GetMapping("admin_and_user")
    public String admin_and_user(){
        return "admin 和 user 角色都可以訪問";
    }
    @GetMapping("admin_only")
    @Authorize(roles = "admin") //覆蓋Controller的設置
    public String admin_only(){
        return "只有 admin 角色可以訪問";
    }
    @GetMapping("public_all")
    @Authorize(anonymous = true) 
    public String public_all(){
        return "匿名可以訪問";
    }
}

不用 Spring Security 和 Shiro ,是不是更簡單呢!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、認證組件 簡介: 登錄認證的限制 認證組件是drf框架給我們提供的認證介面,它能夠在請求進入視圖函數/類前進驗證(例如:認證用戶是否登錄),對不符合認證的請求進行攔截並返回校驗失敗的信息 (1)、登錄介面 # 認證是基於登錄的介面上面操作的 所以前戲編寫一個簡單的登錄介面 models.py c ...
  • 視圖和視圖解析器 1.基本介紹 在SpringMVC中的目標方法,最終返回的都是一個視圖(有各種視圖) 註意,這裡的視圖是一個類對象,不是一個頁面!! 返回的視圖都會由一個視圖解析器來處理(視圖解析器有很多種) 2.自定義視圖 2.1為什麼需要自定義視圖 在預設情況下,我們都是返回預設的視圖,然後返 ...
  • 這篇文章主要描述分散式技術中的選舉演算法,分散式選舉是為選出一個主節點,由它來協調和管理其他節點,保證集群有序運行和節點間數據的一致性。涉及到的選舉演算法包括Bully演算法、Raft演算法和ZAB演算法。 ...
  • 最近在寫一本Xilinx的FPGA方面的書,現將HLS部分內容在這裡分享給大家,希望大家喜歡,也歡迎批評指正。[原創www.cnblogs.com/helesheng] 在可編程邏輯器件被用於電子系統設計的前期,由於所含的邏輯資源較少,絕大部分情況下,它們被用於實現數據的傳輸和介面電路。工程師們習慣 ...
  • 1 簡介 AOP,即面向切麵編程是很常用的技術,特別是在Java Web開發中。而最流行的AOP框架分別是Spring AOP和AspectJ。 2 Spring AOP vs AspectJ Spring AOP是基於Spring IoC實現的,它解決大部分常見的需求,但它並不是一個完整的AOP解 ...
  • 原文地址:https://bysocket.com/openai-chatgpt-error-1020/ 最近打開 chat.openai.com/chat 地址,如下圖會提示錯誤碼:Access denied Error code 1020。這裡通過學習和排查,總結了 3 種方法區解決這個問題。 ...
  • 這次設計一個VGA、TFT顯示模塊,其特點如下: 行同步信號、場同步信號、數據有效信號的延遲數可調。(應用時方便與存儲模塊數據對齊) 解析度可以通過調整參數來改變。 數據格式為RGR565,可簡單修改位寬來修改成其他數據格式。 TFT的介面時序和VGA的時序相似,但是TFT介面比VGA多了數據有效信 ...
  • spi是原生java的組件,通過META-INF/services目錄進行註冊,通過ServiceLoader進行載入,一般可以用在組件開發中,你在公用組件中封裝好邏輯,將個性化的部分抽象出一個介面,介面通過spi的方式進行載入,在外部開發人員引用你的組件之後,通過實現介面來擴展個性化的功能,再通過 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...