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的过程。

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

相关推荐
xcl09256 分钟前
民宿预约系统开发实战:从需求分析到上线部署全流程解析
java·spring boot
梦在远山后10 分钟前
Python 中字符串常用写法
人工智能·后端
geovindu11 分钟前
python:HandWriting Recognition using paddlepaddle
开发语言·后端·python·paddlepaddle
步行cgn20 分钟前
为何以继承方式引入SpringBoot
java·spring boot·后端
CoderYanger21 分钟前
A.每日一题:1140. 石子游戏 II
java·程序人生·算法·leetcode·游戏·职场和发展·深度优先
禾高网络22 分钟前
养老系统开发:背景、架构与养老行业展望
java·大数据·人工智能·小程序·架构
Mr_hou36 分钟前
左手.NET右手Node:一个后端开发的双修之路
后端
wno7041 小时前
Spring Boot配合Hibernate Validator参数校验
spring boot·后端·hibernate
CCYe、1 小时前
新模型上线、旧模型下线:企业AI网关如何管住模型版本
java·网络·数据库·人工智能
风筝在晴天搁浅1 小时前
解题代码清爽版
java·开发语言