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

SpringBoot3.0集成Jwt實(shí)現(xiàn)token驗(yàn)證過程

 更新時(shí)間:2025年12月24日 09:52:50   作者:愛寫代碼的陌  
文章介紹了如何在Spring Boot項(xiàng)目中實(shí)現(xiàn)JWT(JSON Web Token)的認(rèn)證與授權(quán),主要內(nèi)容包括引入依賴、創(chuàng)建工具類、添加攔截器進(jìn)行token校驗(yàn)、登錄接口生成token、定義UserProvider解析用戶信息、自定義異常處理以及如何更新token的Audience以避免token泄露

一、pom.xml引入依賴

<dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
</dependency>

二、創(chuàng)建工具類JwtUtils

public class JwtUtils {
    private static final HashMap<String, String> _audiences = new HashMap<>();

    public static String createJWT(UserProviderDto user) {
        // 指定簽名的時(shí)候使用的簽名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        HashMap<String, Object> claims = new HashMap<>();
        claims.put(Const.JWT_CLAIMS_USER, JSON.toJSONString(user));

        // 設(shè)置jwt的body
        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setSubject(user.getUsername())
                .setAudience(updateAudience(user.getUsername()))
                .setIssuedAt(new Date())
                // 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰
                .signWith(signatureAlgorithm, Const.JWT_SECRET_KEY.getBytes(StandardCharsets.UTF_8))
                // 設(shè)置過期時(shí)間
                .setExpiration(TimeUtils.getTodayEnd());

        return builder.compact();
    }

    /**
     * Token解密
     */
    public static Claims parseJWT(String token) {
        try {
            // 得到DefaultJwtParser
            return Jwts.parser()
                    // 設(shè)置簽名的秘鑰
                    .setSigningKey(Const.JWT_SECRET_KEY.getBytes(StandardCharsets.UTF_8))
                    // 設(shè)置需要解析的jwt
                    .parseClaimsJws(token)
                    .getBody();
        }
        catch (Exception ex) {
            return null;
        }
    }

    /**
     * 更新當(dāng)前登錄賬號(hào)的最新狀態(tài)
     * @param userNo 用戶名
     * @return 狀態(tài)標(biāo)志
     */
    public static String updateAudience(String userNo) {
        if (LogwingStringUtils.isNullOrEmpty(userNo))
            return "";

        var audience = userNo + TimeUtils.getCurrentTimeStamp();
        _audiences.put(userNo, audience);

        return audience;
    }

    /**
     * 判斷當(dāng)前token是否最新的,不是需重新登錄
     * @return boolean
     */
    public static boolean isNewestAudience(String userNo, String audience) {
        if (LogwingStringUtils.isNullOrEmpty(userNo) || LogwingStringUtils.isNullOrEmpty(audience))
            return false;

        if (!_audiences.containsKey(userNo))
            return false;

        return _audiences.get(userNo).equals(audience);
    }

    /**
     * 獲取token
     * @param request 當(dāng)前http請(qǐng)求
     * @return token
     */
    public static String getToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (LogwingStringUtils.isNullOrEmpty(token)) {
            return "";
        }

        token = token.replace("Bearer ", "");
        if (LogwingStringUtils.isNullOrEmpty(token)) {
            return "";
        }

        return token;
    }
}

三、添加全局?jǐn)r截器,校驗(yàn)token是否有效

public class JwtInterceptor implements HandlerInterceptor {
    //主要功能攔截請(qǐng)求驗(yàn)證token
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = JwtUtils.getToken(request);
        if (LogwingStringUtils.isNullOrEmpty(token)) {
            throw new InvalidUserException();
        }

        var claims = JwtUtils.parseJWT(token);
        if (claims == null) {
            throw new InvalidUserException();
        }

        Date createTime = claims.getIssuedAt();
        if (createTime == null || createTime.compareTo(TimeUtils.getTodayStart()) < 0) {
            throw new InvalidUserException();
        }

        String audience = claims.getAudience();
        if (!JwtUtils.isNewestAudience(claims.getSubject(), audience)) {
            throw new InvalidUserException();
        }

        return true;
    }
}

四、將攔截器添加到WebConfig配置中

攔截請(qǐng)求進(jìn)行token校驗(yàn)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JwtInterceptor())
                //攔截的路徑
                .addPathPatterns("/**")
                //不需要的攔截請(qǐng)求
                .excludePathPatterns(
                        "/api/OAuth/login",
                        "/api/OAuth/logout",
                        "/api/getDate",
                        "/swagger-ui/**",
                        "/v3/**");
    }
}

五、登錄接口增加生成token

返回前端,賬號(hào)密碼校驗(yàn)完成之后生成token,將用戶基本信息存入token

public LoginOutputDto login(LoginUserInputDto input) {
        Admin loginUser = adminRepository.getLoginUserByUserName(input.getUserName());
        if (loginUser == null)
            return new LoginOutputDto(false, "用戶名不存在,請(qǐng)重新輸入。");

        var inputPassword = RSAUtils.decryptByRSA(input.getEncryptedPassword());
        var encryptPassword = MD5Utils.encryptMD5(MD5Utils.encryptMD5(inputPassword) + loginUser.getSalt());
        if (!encryptPassword.equals(loginUser.getPassword()))
            return new LoginOutputDto(false, "密碼有誤,請(qǐng)重新輸入。");

        UserProviderDto userProvider = getUserProvider(loginUser);
        String token = JwtUtils.createJWT(userProvider);
        LoginOutputDto output = new LoginOutputDto(true, "登錄成功。");
        output.setAccessToken(token);

        return output;
    }

    private UserProviderDto getUserProvider(Admin loginUser) {
        UserProviderDto userProvider = new UserProviderDto();
        userProvider.setId(loginUser.getId());
        userProvider.setUsername(loginUser.getUsername());
        userProvider.setStatus(loginUser.getStatus());
        userProvider.setType(loginUser.getType());

        return userProvider;
    }
}

六、定義UserProvider

提供從token解析出當(dāng)前用戶信息的接口方法

@Service
public class UserProvider implements IUserProvider {
    @Autowired
    private HttpServletRequest httpServletRequest;

    @Override
    public UserProviderDto getCurrentUser() {
        String token = JwtUtils.getToken(httpServletRequest);
        if (LogwingStringUtils.isNullOrEmpty(token)) {
            throw new RuntimeException(Const.UN_LOGIN_TIPS);
        }

        Claims claims = JwtUtils.parseJWT(token);
        if (claims == null) {
            throw new RuntimeException(Const.UN_LOGIN_TIPS);
        }

        Date createTime = claims.getIssuedAt();
        if (createTime == null || createTime.compareTo(TimeUtils.getTodayStart()) < 0) {
            throw new RuntimeException(Const.UN_LOGIN_TIPS);
        }

        String user = (String) claims.get(Const.JWT_CLAIMS_USER);

        return JSON.parseObject(user, UserProviderDto.class);
    }
}

七、定義controller接口

登錄接口

@PostMapping("/api/OAuth/login")
@ResponseBody
@Operation(summary = "登錄授權(quán)接口", description = "登錄授權(quán)接口")
public LoginOutputDto login(@Valid @RequestBody LoginUserInputDto input) throws NoSuchAlgorithmException {
    return loginService.login(input);
}

退出接口

退出時(shí)更新當(dāng)前賬號(hào)的Audience,用來避免退出后用舊的token仍然可以通過接口token是否有效的攔截(因?yàn)閖wt具有無狀態(tài)性,生成token之后改token只能等設(shè)置的過期時(shí)間到了之后才能自動(dòng)銷毀,否則該token在過期時(shí)間之前仍然有效),具體方法見jwtutils中的updateaudience和isnewestaudience方法

@PostMapping("/api/OAuth/logout")
@ResponseBody
@Operation(summary = "退出接口")
public ResponseOutputDto logout() {
    var user = userProvider.getCurrentUser();
    JwtUtils.updateAudience(user.getUsername());

    return new ResponseOutputDto();
}

八、自定義異常InvalidUserException

并攔截全局異常InvalidUserException,捕捉攔截器拋出的異常,返回給前端

public class InvalidUserException extends Exception {
    public InvalidUserException() {
        super(Const.UN_LOGIN_TIPS);
    }
}
@ControllerAdvice
@Slf4j
public class SystemExceptionHandler {
    @ExceptionHandler(InvalidUserException.class)
    @ResponseBody
    public LoginOutputDto doException(InvalidUserException ex) {
        return new LoginOutputDto(false, ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseOutputDto doException(Exception ex) {
        if (ex.getMessage().contains("未登錄")) {
            return new ResponseOutputDto(false, ex.getMessage());
        }

        log.error(ex.getMessage(), ex);
        return new ResponseOutputDto(false, "程序發(fā)生錯(cuò)誤,請(qǐng)聯(lián)系客服。");
    }
}

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot利用Junit動(dòng)態(tài)代理實(shí)現(xiàn)Mock方法

    SpringBoot利用Junit動(dòng)態(tài)代理實(shí)現(xiàn)Mock方法

    說到Spring Boot 單元測(cè)試主要有兩個(gè)主流集成分別是Mockito,Junit,這個(gè)各有特點(diǎn),在實(shí)際開發(fā)中,我想要的測(cè)試框架應(yīng)該是這個(gè)框架集成者,本文給大家介紹了SpringBoot利用Junit動(dòng)態(tài)代理實(shí)現(xiàn)Mock方法,需要的朋友可以參考下
    2024-04-04
  • java連接Mysql數(shù)據(jù)庫(kù)的工具類

    java連接Mysql數(shù)據(jù)庫(kù)的工具類

    這篇文章主要介紹了java連接Mysql數(shù)據(jù)庫(kù)的工具類,非常的實(shí)用,推薦給大家,需要的朋友可以參考下
    2015-03-03
  • Java中BorderLayout布局管理器的兩種排列方式

    Java中BorderLayout布局管理器的兩種排列方式

    這篇文章主要介紹了Java中BorderLayout布局管理器的兩種排列方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 超詳細(xì)講解Java秒殺項(xiàng)目登陸模塊的實(shí)現(xiàn)

    超詳細(xì)講解Java秒殺項(xiàng)目登陸模塊的實(shí)現(xiàn)

    這是一個(gè)主要使用java開發(fā)的秒殺系統(tǒng),項(xiàng)目比較大,所以本篇只實(shí)現(xiàn)了登陸模塊,代碼非常詳盡,感興趣的朋友快來看看
    2022-03-03
  • SpringBoot項(xiàng)目加載配置文件的6種方式小結(jié)

    SpringBoot項(xiàng)目加載配置文件的6種方式小結(jié)

    這篇文章給大家總結(jié)了六種SpringBoot項(xiàng)目加載配置文件的方式,通過@value注入,通過@ConfigurationProperties注入,通過框架自帶對(duì)象Environment實(shí)現(xiàn)屬性動(dòng)態(tài)注入,通過@PropertySource注解,yml外部文件,Java原生態(tài)方式注入這六種,需要的朋友可以參考下
    2023-09-09
  • 詳解Java Fibonacci Search斐波那契搜索算法代碼實(shí)現(xiàn)

    詳解Java Fibonacci Search斐波那契搜索算法代碼實(shí)現(xiàn)

    這篇文章主要介紹了詳解Java Fibonacci Search斐波那契搜索算法代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Java?中的異常處理機(jī)制詳情介紹

    Java?中的異常處理機(jī)制詳情介紹

    本篇文章主要介紹Java中的異常、如何處理函數(shù)拋出的異常、處理異常的原則、異常處理時(shí),性能開銷大的地方,感興趣的小伙伴可以參考一下
    2022-09-09
  • Springboot項(xiàng)目參數(shù)校驗(yàn)方式(Validator)

    Springboot項(xiàng)目參數(shù)校驗(yàn)方式(Validator)

    本文介紹了如何在Spring Boot項(xiàng)目中使用`spring-boot-starter-validation`包和注解來實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn),主要介紹了校驗(yàn)注解的使用方法、校驗(yàn)失敗的異常捕獲以及`@Validated`的分組功能
    2025-02-02
  • JAVA DOM解析XML文件過程詳解

    JAVA DOM解析XML文件過程詳解

    這篇文章主要介紹了JAVA DOM解析XML文件過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java算法題常用函數(shù)詳解

    Java算法題常用函數(shù)詳解

    這篇文章主要介紹了Java中常用的字符串操作、字符串轉(zhuǎn)換、字符串處理、字符串緩沖區(qū)、棧、數(shù)組、列表、隊(duì)列、優(yōu)先隊(duì)列、Map和HashMap的常用函數(shù)和操作,感興趣的朋友跟隨小編一起看看吧
    2025-11-11

最新評(píng)論

七台河市| 克山县| 贵南县| 彭阳县| 和林格尔县| 搜索| 黄浦区| 景德镇市| 麟游县| 云南省| 丰台区| 宁陵县| 朝阳市| 田林县| 乌鲁木齐县| 井陉县| 浦县| 萝北县| 从化市| 阿拉善盟| 内乡县| 秦皇岛市| 五指山市| 黄山市| 东源县| 天全县| 吉木乃县| 天等县| 尚志市| 阿坝县| 柯坪县| 堆龙德庆县| 资阳市| 儋州市| 霍城县| 新安县| 肃宁县| 东乌珠穆沁旗| 新建县| 金坛市| 始兴县|