SpringBoot(二十三)SpringBoot集成JWT

最近整理完docker之后,突然想到,我是不是可以使用docker部署多个blog实例,来实现一下负载均衡呢?

现阶段,blog项目使用的是SESSION来做用户登录信息存储,如果配置负载均衡的话,那session其实就不是很适用了。没有办法跨实例共享。

那么该怎么办呢?很简单,使用JWT,那么何为JWT呢?

JWT(JSON WEB TOKEN)是一种开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间以 JSON 对象的形式安全地传输信息。此信息可以验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSAECDSA 的公钥/私钥对进行签名。

上边这段话是我从百度上复制下来的。JWT简单讲就是一个加密字符串,跨语言,有过期时间。

下面我需要将现在blog中的session部分改成JWT来存储。

一:添加依赖

<!-- jwt       -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

二:添加JwtUtils .java

java 复制代码
package com.springbootblog.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JwtUtils
{
    //jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
    private static final String secret_key = "camellia";
    // jwt过期时间(毫秒)
    private static final int ttl = 43200 * 1000;
    public static final String token_name = "token";

    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(Map<String, Object> claims)
    {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        Date nowDate = new Date();
        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + JwtUtils.ttl;
        Date exp = new Date(expMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 标记生成时间
                .setIssuedAt(nowDate)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, JwtUtils.secret_key.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();
    }

    /**
     * 重新生成token,根据之前的生成时间和过期时间
     * @param claims
     * @return
     */
    public static String createJWTagain(Map<String, Object> claims,Date nowDate,Date expiration)
    {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 标记生成时间
                .setIssuedAt(nowDate)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, JwtUtils.secret_key.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(expiration);

        return builder.compact();
    }

    /**
     * Token解密
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT( String token)
    {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(JwtUtils.secret_key.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

    /**
     * 验证token是否过期失效
     * @param expirationTime
     * @return
     */
    public static boolean isTokenExpired (Date expirationTime) {
        return expirationTime.before(new Date());
    }

    /**
     * 获取jwt发布时间
     * @param token     token字符串
     */
    public static Date getIssuedAtDateFromToken(String token) {
        return parseJWT(token).getIssuedAt();
    }

    /**
     * 获取jwt发布时间
     * @param token     token字符串
     */
    public static Date getExpirationDateFromToken(String token) {
        return parseJWT(token).getExpiration();
    }
}

三:后端测试TOKEN生成及解析

java 复制代码
@GetMapping("index/login")
    public Map<String, Object> login (@RequestParam("userName") String userName, @RequestParam("passWord") String passWord){
        // 声明返回map
        Map<String, Object> result = new HashMap<>() ;
        // 声明加密参数map
        Map<String, Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("username","camellia");
        claims.put("url","https://guanchao.site");
        claims.put("age",25);
        String token= JwtUtils.createJWT(claims);
        //System.out.println("生成的jwt:"+token);

        Claims paramList = JwtUtils.parseJWT(token);
        System.out.println("解析后的token:"+paramList);
        System.out.println("解析后的token的id"+paramList.get("id"));
        System.out.println("解析后的token的有效期"+paramList.getExpiration());
        System.out.println("解析后的token的url"+paramList.get("url"));
        result.put("code", 200);
        result.put("token", token);
        return result ;
    }

理论上来说,到这里,后端的jwt集成就成功了。

四:前端VUE集成JWT

我的逻辑是,在登录成功接口中获取后端生成的TOKEN。将其存储到浏览器的缓存localstroage中。

每次请求的时候将TOKEN值从localstroage中取出,放入axios请求的header中。这部分放在axios封装中。

具体代码如何操作,请自行百度,我这里不做展示。

五:前端集成JWT后,后端测试JWT

java 复制代码
public Map<String, Object> getFooterData(HttpServletRequest request)
{
    // 获取token
    String token = request.getHeader(JwtUtils.token_name);
    Object userid = "";
    Object figureurl = "";
    Object nickname = "";
    Object email = "";
    if(token.equals("") || token.equals("undefined") || token.equals("null") || token.equals(null))
    {}
    else
    {
        // 解析token
        Claims paramList = JwtUtils.parseJWT(token);
        userid = paramList.get("id");
        figureurl = paramList.get("figureurl");
        nickname = paramList.get("nickname");
        email = paramList.get("email");
    }

    Map<String,Object> result = new HashMap<>(12);
    // result.put("token",token);
    result.put("code",1);
    result.put("msg","操作成功!");
    result.put("visit",visit);
    result.put("friendLinklist",friendLinklist);
    result.put("article",article);
    result.put("user",user);
    result.put("message",message);
    result.put("userid",userid);
    result.put("figureurl",figureurl == null ? "" : figureurl);
    result.put("nickname",nickname);
    result.put("email",email);
    result.put("days",days);
    return result;
}

主要的部分都有注释,参照即可。

六:一些小问题

我的项目使用的是openjdk11,openjdk11集成JWT的时候发现一个小报错:

java 复制代码
javax/xml/bind/DatatypeConverter

解决方式很简单,添加几个依赖就可以了:

java 复制代码
<!-- jaxb依赖包 -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.4.0-b180830.0438</version>
</dependency>

以上大概就是Springboot集成JWT的过程。

有好的建议,请在下方输入你的评论。

相关推荐
SomeB1oody几秒前
【Rust自学】7.3. use关键字 Pt.1:use的使用与as关键字
开发语言·后端·rust
DARLING Zero two♡7 分钟前
【优选算法】Sliding-Chakra:滑动窗口的算法流(上)
java·开发语言·数据结构·c++·算法
minstbe10 分钟前
WEB开发 - Flask 入门:Jinja2 模板语法进阶 Python
后端·python·flask
love静思冥想12 分钟前
Apache Commons ThreadUtils 的使用与优化
java·线程池优化
君败红颜14 分钟前
Apache Commons Pool2—Java对象池的利器
java·开发语言·apache
意疏22 分钟前
JDK动态代理、Cglib动态代理及Spring AOP
java·开发语言·spring
hjxxlsx23 分钟前
什么是Spring Boot 应用开发?
spring boot
小王努力学编程24 分钟前
【C++篇】AVL树的实现
java·开发语言·c++
找了一圈尾巴35 分钟前
Wend看源码-Java-集合学习(List)
java·学习
无名之逆36 分钟前
lombok-macros
开发语言·windows·后端·算法·面试·rust·大学期末