B093-springsecurity整合jwt和RSA

目录

      • 前后端分离后springsecurity核心filter的应用场景介绍
      • JWT令牌的组成部分
      • JWT案例
        • 导包
        • TestJwt
      • RSA
        • RsaUtils
        • TestRSA
        • 分析图
      • JWT+RSA
        • 导包
        • JwtUtils
        • TestRSAJWT
      • 完善spring-security整合后且不连数据库的代码案例
      • 流程
        • 分析图

前后端分离后springsecurity核心filter的应用场景介绍

B093-springsecurity整合jwt和RSA_第1张图片
B093-springsecurity整合jwt和RSA_第2张图片
B093-springsecurity整合jwt和RSA_第3张图片
账号密码经过UsernamePasswordAuthenticationFilter封装为UsernamePasswordAuthenticationToken之后密码为二进制就拿不到也无法解密了

/login 由Security框架处理
B093-springsecurity整合jwt和RSA_第4张图片
用户登录之后的权限列表可以存在JWT里

JWT令牌的组成部分

头部:头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

载荷:装数据的地方,

签名:放秘钥的地方,这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

JWT案例

可以携带数据,可以解密

导包

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-testartifactId>
dependency>

<dependency>
	<groupId>com.auth0groupId>
	<artifactId>java-jwtartifactId>
	<version>3.4.0version>
dependency>

<dependency>
	<groupId>org.apache.commonsgroupId>
	<artifactId>commons-lang3artifactId>
dependency>
<dependency>
	<groupId>org.projectlombokgroupId>
	<artifactId>lombokartifactId>
dependency>
<dependency>
	<groupId>com.alibabagroupId>
	<artifactId>fastjsonartifactId>
	<version>1.2.68version>
dependency>

TestJwt

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.time.DateUtils;
import org.example.domain.User;
import org.junit.Test;
import java.util.Date;

public class TestJwt {

    @Test
    public void createJwt(){
        String sign = JWT.create()
                //.withHeader()可以不给,默认即可
                //添加jwt的载荷信息,使用的api方法withClaim(),这个方法在添加数据是以 key-value键值对格式进行添加
                //本质上,载荷中的信息类似于一个map类型
                .withClaim("account", "135123123")
                .withClaim("userId", "uuid")
                .withClaim("age", 12)
                //jwt可以对载荷中的信息,进行有效时间的设置。下面的设置表示:当前jwt令牌有效时间:3分钟
                .withExpiresAt(DateUtils.addMinutes(new Date(), 3))
                //设置的值即为  密钥
                .sign(Algorithm.HMAC256("##@@!!"));
        System.out.println(sign);
        //eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 头部信息经过base64转码之后的字符串
        // .eyJleHAiOjE2MTkwNTc2OTIsInVzZXJJZCI6InV1aWQiLCJhY2NvdW50IjoiMTM1MTIzMTIzIiwiYWdlIjoxMn0 载荷进行base64加密之后字符串
        // .JCagRTvVd_dshxGF214TpbGDNIvsj3Lift9x2ZZ-vZw 签证base64加密之后的字符串
    }

    @Test
    public void 解析jwt的载荷(){
        String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2OTM3OTg3MzgsInVzZXJJZCI6InV1aWQiLCJhY2NvdW50IjoiMTM1MTIzMTIzIiwiYWdlIjoxMn0.AuyeuCeB3OSbmUKjz66tOXrP3yi9uosJdBQOgF2mSuY";
        //##@@!! 它就是生成jwt时的密钥
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("##@@!!")).build();//先验签
        DecodedJWT verify = jwtVerifier.verify(jwt);
        System.out.println(verify.getClaim("account").asString());
        System.out.println(verify.getClaim("userId").asString());
        System.out.println(verify.getClaim("age").asInt());
    }

    @Test //传对象
    public void createJwt2() {
        //jwt需要保存user对象的信息
        User user = new User();
        user.setUserName("admin");
        user.setPassword("123456");
        user.setAge(22);
        String sign = JWT.create()
                //.withHeader()可以不给,默认即可
                //添加jwt的载荷信息,使用的api方法withClaim(),这个方法在添加数据是以 key-value键值对格式进行添加
                //本质上,载荷中的信息类似于一个map类型
                .withClaim("user", JSONObject.toJSONString(user))
                //jwt可以对载荷中的信息,进行有效时间的设置。下面的设置表示:当前jwt令牌有效时间:3分钟
                .withExpiresAt(DateUtils.addMinutes(new Date(), 3))
                //设置的值即为  密钥
                .sign(Algorithm.HMAC256("##@@!!999"));
        System.out.println(sign);
        //eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 头部信息经过base64转码之后的字符串
        // .eyJleHAiOjE2MTkwNTc2OTIsInVzZXJJZCI6InV1aWQiLCJhY2NvdW50IjoiMTM1MTIzMTIzIiwiYWdlIjoxMn0 载荷进行base64加密之后字符串
        // .JCagRTvVd_dshxGF214TpbGDNIvsj3Lift9x2ZZ-vZw 签证base64加密之后的字符串
    }

    @Test
    public void 解析jwt的载荷2(){
        String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2OTM4MDAxMzYsInVzZXIiOiJ7XCJhZ2VcIjoyMixcInBhc3N3b3JkXCI6XCIxMjM0NTZcIixcInVzZXJOYW1lXCI6XCJhZG1pblwifSJ9.y2Q7osdt1gtUff8yvHBBK1qjMUvA6JvqBSqDiZ7XcQw";
        //##@@!! 它就是生成jwt时的密钥
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("##@@!!999")).build();//先验签
        DecodedJWT verify = jwtVerifier.verify(jwt);
        System.out.println(verify.getClaim("user").asString());
    }
}

RSA

jwt在不同系统间交互时需要让对方知道秘钥对方才能解密读取信息,这样还是不够安全,所以需要RSA

RsaUtils

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtils {
    private static final int DEFAULT_KEY_SIZE = 2048;

    /**
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象0
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        bytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        bytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String
            secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}

TestRSA

import org.example.utils.RsaUtils;
import org.junit.Test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class TestRSA {

    @Test
    public void testpass(){
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode("123456");
        System.out.println(encode);
    }

    @Test
    public void 创建公私钥对() throws Exception {
        //String privateFilePath = ResourceUtils.getFile("classpath:rsa").getPath();
        //String publicFilePath = ResourceUtils.getFile("classpath:rsa.pub").getPath();
        String privateFilePath = "D:\\(课件 Xmind 图 代码) (总结) (原理)(题目) (预习)\\093-springsecurity整合jwt和RSA\\rsa.pri";
        String publicFilePath = "D:\\(课件 Xmind 图 代码) (总结) (原理)(题目) (预习)\\093-springsecurity整合jwt和RSA\\rsa.pub";

        //生成密钥对的公共方法。这个方法需要传入4个参数
        /*
            参数1:生成公钥的存储位置
            参数2:生成私钥的存储位置
            参数3:生成密钥对的密钥
            参数3:生成密钥对的长度
         */
        RsaUtils.generateKey(publicFilePath, privateFilePath, "itsource", 2048);
        //获取私钥
        System.out.println(RsaUtils.getPrivateKey(privateFilePath));
        //获取公钥
        System.out.println(RsaUtils.getPublicKey(publicFilePath));
    }
}

分析图

B093-springsecurity整合jwt和RSA_第5张图片

JWT+RSA

导包

<dependency>
    <groupId>io.jsonwebtokengroupId>
    <artifactId>jjwt-apiartifactId>
    <version>0.10.7version>
dependency>
<dependency>
    <groupId>io.jsonwebtokengroupId>
    <artifactId>jjwt-implartifactId>
    <version>0.10.7version>
dependency>
<dependency>
    <groupId>io.jsonwebtokengroupId>
    <artifactId>jjwt-jacksonartifactId>
    <version>0.10.7version>
dependency>

JwtUtils

import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;

/**
 * 生成token以及校验token相关方法
 */
public class JwtUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    /**
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间,单位分钟
     * @return JWT
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        //计算过期时间
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MINUTE, expire);

        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JSONObject.toJSONString(userInfo))
                .setId(new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes())))
                .setExpiration(c.getTime())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间,单位秒
     * @return JWT
     */
    public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
        //计算过期时间
        Calendar c = Calendar.getInstance();
        c.add(Calendar.SECOND, expire);

        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JSONObject.toJSONString(userInfo))
                .setId(new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes())))
                .setExpiration(c.getTime())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static Object getInfoFromToken(String token, PublicKey publicKey, Class userType) {
        //解析token
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);

        Claims body = claimsJws.getBody();
        String userInfoJson = body.get(JWT_PAYLOAD_USER_KEY).toString();
        return JSONObject.parseObject(userInfoJson, userType);
    }
}

TestRSAJWT

import org.example.utils.JwtUtils;
import org.example.utils.RsaUtils;
import org.junit.Test;
import org.springframework.util.ResourceUtils;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

public class TestRSAJWT {

    @Test //创建基于RSA的jwt令牌
    public void rsaCreateTest() throws Exception{
        //生成jwt令牌的载荷信息
        Map userinfo = new HashMap() {{ //新写法:jdk1.8新特性
            put("account", "jack");
            put("auth", "a,b,c,d");
        }};
        //获取私钥:将生成的私钥保存的文件转换为私钥路径对象
        String path = ResourceUtils.getFile("classpath:rsa.pri").getPath();
        //构建私钥对象 根据路径转换为私钥对象
        PrivateKey privateKey = RsaUtils.getPrivateKey(path);
        //下面的方法,在生成jwt令牌的信息时,是通过RSA的私钥生成
        String token = JwtUtils.generateTokenExpireInMinutes(userinfo, privateKey, 3);
        System.out.println(token);
        // eyJhbGciOiJSUzI1NiJ9
        // .eyJ1c2VyIjoie1wiYXV0aFwiOlwiYSxiLGMsZFwiLFwiYWNjb3VudFwiOlwiamFja1wifSIsImp0aSI6IlpUQmhPVGMwWmpBdFpESmtaUzAwTkdSaExXRmpOVGd0TVRWbE9UZzVOV0ptWm1GayIsImV4cCI6MTYxOTA1OTUzNH0
        // .gohQiN6Dg9x0FnyHJw1ecJLCjnijSG3mFXYGC52ewH7F-xvuiCY7Hr9DF_NtWAdk0LkNg8NUYECnOQicmgBG6kDw56NqiHwn1qT003U83gGaGfM0_fCrSrx-J-Kx9qnHASvULbFPPymgrp3vI7KdAM89fORk8j43sJ-G4ASbEC3R-BkIvlNjNUK3WEQIQ5Yahk4ckHYQQZY0DpxyI-gsRGBB1bf1x0YBTrtM0XTlR2NcIZ6LPPCm84ck8ADsJxrQlG8VgOHUjrO4JAwpNOOGaSrAz37-zoEz4qDwfEKVE5HD7kXWb33bHjCRbipOdSopGz9uezHZyn1HloApiQiFDA
    }

    @Test //将生成的基于RSA的JWT进行解密,获取载荷的数据
    public void rsaRead() throws Exception{
        //解析token
        String token = "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjoie1wiYXV0aFwiOlwiYSxiLGMsZFwiLFwiYWNjb3VudFwiOlwiamFja1wifSIsImp0aSI6IlpESXpNV1F3TURrdE9HSmhZUzAwT0RZNExXRmtOemN0TURBM1lqaGpPREJqWVdGaSIsImV4cCI6MTY5MzgyNTc2Nn0.4NUlipqCiibS2rm0xwFPzCPLwe8go8Ycs9ZGV6UdtS-bdgoa-ZHX2n1q1NajeiRva1koknpVVYnuX-ImRl6WM1JPsZVV8w_WpOvpsohi2OqAo4sAljAx0N7Qo-pLEuUyS6n3zwpgSW-0Z1NNVvCZfPtQZIGHIwk6Cani35EkXMaLhYlec8VGJxdiJKUV4xZDtsa-5-xl5JmLeCA6vjuUcusoGwIjO8j3-0Fd0TCPOwqFYuXA5vcxKM6JfUMjdmKp-BnKsr_65PgP8RtGLjhn037xNjsOLZrxtP2ZjH6rfBCpYE4RPWgt_rV3CA-0n6h_jiyqxYmgm8b4Q6OdSBuhQA";
        //获取公钥路径
        String path1 = ResourceUtils.getFile("classpath:rsa.pub").getPath();
        //构建公钥对象
        PublicKey publicKey = RsaUtils.getPublicKey(path1);

        Map infoFromToken = (Map) JwtUtils.getInfoFromToken(token, publicKey, Map.class);
        System.out.println(infoFromToken.get("account"));
        System.out.println(infoFromToken.get("auth"));
    }
}

完善spring-security整合后且不连数据库的代码案例

见代码

流程

config,sevice&filter,注解

分析图

B093-springsecurity整合jwt和RSA_第6张图片
B093-springsecurity整合jwt和RSA_第7张图片

你可能感兴趣的:(笔记总结,springsecurity,jwt,rsa)