美文网首页springbootspringsecurity
Spring Security-整合Spring Boot(分布

Spring Security-整合Spring Boot(分布

作者: 石头耳东 | 来源:发表于2022-05-19 15:42 被阅读0次

    前置文章:
    Spring Security-整合Spring Boot(分布式)-篇章一:主要介绍了JWT、RSA以及Security的认证校验逻辑;

    前言:本文主要介绍JWT工具类,以及RSA工具类的基础使用。是一些固定的用法,可以跟据需求了解即可。

    零、本文纲要

    • 一、 代码实现
    • 二、 security_parent(聚合管理)
    • 三、 security_common(共用模块)
    1. domain/entity
    2. utils
    3. 使用工具类生成公私钥

    一、 代码实现

    1. 模块构成

    security_parent(聚合管理)
    security_common(共用模块)
    security_auth_server(认证模块)
    security_source_product(资源模块)

    二、 security_parent(聚合管理)

    • ① pom.xml文件
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.stone</groupId>
        <artifactId>springboot_security_jwt_rsa_parent</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
    
        <!--聚合管理-->
        <modules>
            <module>security_common</module>
            <module>security_auth_server</module>
            <module>security_source_product</module>
        </modules>
    
        <!--统一依赖管理-->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
            <relativePath/>
        </parent>
    
    </project>
    

    三、 security_common(共用模块)

    1. domain/entity

    JWT: Header.Payload.Signature

    此处我们自定义Payload类,用于承载我们想要存储在Token中的信息;
    Payload包含:id、用户信息、过期时间。

    @Data
    public class Payload<T> {
        private String id;
        private T userInfo;
        private Date expiration;
    }
    

    2. utils

    注意:此处的工具类主要是把具体类的固定用法做了封装,实际按需了解即可。

    • ① JsonUtils
    public class JsonUtils {
    
        public static final ObjectMapper mapper = new ObjectMapper();
    
        private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
    
        public static String toString(Object obj) {
            if (obj == null) {
                return null;
            }
            if (obj.getClass() == String.class) {
                return (String) obj;
            }
            try {
                return mapper.writeValueAsString(obj);
            } catch (JsonProcessingException e) {
                logger.error("json序列化出错:" + obj, e);
                return null;
            }
        }
    
        public static <T> T toBean(String json, Class<T> tClass) {
            try {
                return mapper.readValue(json, tClass);
            } catch (IOException e) {
                logger.error("json解析出错:" + json, e);
                return null;
            }
        }
    
        public static <E> List<E> toList(String json, Class<E> eClass) {
            try {
                return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));
            } catch (IOException e) {
                logger.error("json解析出错:" + json, e);
                return null;
            }
        }
    
        public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> vClass) {
            try {
                return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));
            } catch (IOException e) {
                logger.error("json解析出错:" + json, e);
                return null;
            }
        }
    
        public static <T> T nativeRead(String json, TypeReference<T> type) {
            try {
                return mapper.readValue(json, type);
            } catch (IOException e) {
                logger.error("json解析出错:" + json, e);
                return null;
            }
        }
    }
    
    • ② JwtUtils
    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) {
            return Jwts.builder() //DefaultJwtBuilder
                    .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo)) //DefaultClaims#LinkedHashMap
                    .setId(createJTI()) //key:"jti",value:createJTI()
                    .setExpiration(DateTime.now().plusMinutes(expire).toDate()) //key:"exp",value:"minutes"
                    .signWith(privateKey, SignatureAlgorithm.RS256) //设置加密算法 和 key
                    .compact(); //进行加密,返回jwt
        }
    
        /**
         * 私钥加密token
         *
         * @param userInfo   载荷中的数据
         * @param privateKey 私钥
         * @param expire     过期时间,单位[秒]
         * @return JWT
         */
        public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
            return Jwts.builder()
                    .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
                    .setId(createJTI())
                    .setExpiration(DateTime.now().plusSeconds(expire).toDate())
                    .signWith(privateKey, SignatureAlgorithm.RS256)
                    .compact();
        }
    
        /**
         * 公钥解析token
         *
         * @param token     用户请求中的token
         * @param publicKey 公钥
         * @return Jws<Claims>
         */
        private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
            return Jwts.parser() //DefaultJwtParser
                .setSigningKey(publicKey) //公钥解密
                .parseClaimsJws(token); //需要解密的token
        }
    
        private static String createJTI() {
            return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
        }
    
        /**
         * 获取token中的用户信息
         *
         * @param token     用户请求中的令牌
         * @param publicKey 公钥
         * @return 用户信息
         */
        public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
            Jws<Claims> claimsJws = parserToken(token, publicKey);
            Claims body = claimsJws.getBody();
            Payload<T> claims = new Payload<>();
            claims.setId(body.getId());
            claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
            claims.setExpiration(body.getExpiration());
            return claims;
        }
    
        /**
         * 获取token中的载荷信息
         *
         * @param token     用户请求中的令牌
         * @param publicKey 公钥
         * @return 用户信息
         */
        public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
            Jws<Claims> claimsJws = parserToken(token, publicKey);
            Claims body = claimsJws.getBody();
            Payload<T> claims = new Payload<>();
            claims.setId(body.getId());
            claims.setExpiration(body.getExpiration());
            return claims;
        }
    }
    
    • ③ RsaUtils
    public class RsaUtils {
    
        private static final int DEFAULT_KEY_SIZE = 2048;
        /**
         * 从文件中读取公钥
         *
         * @param filename 公钥保存路径,相对于classpath
         * @return 公钥对象
         * @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
         */
        private static PublicKey getPublicKey(byte[] bytes) throws Exception {
            bytes = Base64.getDecoder().decode(bytes);
            //用给定的编码密钥创建一个新的X509EncodedKeySpec,按照X.509标准编码的密钥
            X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            //生成公钥
            return factory.generatePublic(spec);
        }
    
        /**
         * 获取密钥
         *
         * @param bytes 私钥的字节形式
         * @return
         * @throws Exception
         */
        private 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 {
            //指定使用RSA算法
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            //将指定的"盐"封装到SecureRandom对象
            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);
        }
    }
    

    3. 使用工具类生成公私钥

    此处以win系统为例,实际业务需调整Linux目录

    public class RsaUtilsTest extends TestCase {
    
        private String privateFilePath = "D:\\tmp\\auth\\id_key_rsa.pri";
        private String publicFilePath = "D:\\tmp\\auth\\id_key_rsa.pub";
    
        public void testGetPublicKey() throws Exception {
            System.out.println(RsaUtils.getPublicKey(publicFilePath));
        }
    
        public void testGetPrivateKey() throws Exception {
            System.out.println(RsaUtils.getPrivateKey(privateFilePath));
        }
    
        public void testGenerateKey() throws Exception {
            RsaUtils.generateKey(publicFilePath, privateFilePath, "salt", 2048);
        }
    }
    

    四、结尾

    以上即为Spring Security-整合Spring Boot(分布式)-篇章二的全部内容,感谢阅读。

    相关文章

      网友评论

        本文标题:Spring Security-整合Spring Boot(分布

        本文链接:https://www.haomeiwen.com/subject/gtqtprtx.html