用starter实现api接口的加密与日志功能

news2024/10/5 12:58:36

一、概述

运用AOP技术实现对api接口的加密及日志功能。

  • 加密
    • 需要加密的api接口上加注解:@Encrypt(自定义注解)
    • 接口返回类型为String时才加密
    • 采用对称加密:加密和解密使用相同的密钥
  • 日志

对所有的api接口添加日志功能:记录接口的执行时间

  • 服务请求、处理的基本流程

在这里插入图片描述

二、制作starter

制作过程参考:

  • 【SpringBoot】自定义启动器 Starter【保姆级教程】
  • 用starter实现Oauth2中资源服务的统一配置
  • 用spring-boot-starter实现事务的统一配置

1、总体结构

在这里插入图片描述

2、外部引用模块

名称:tuwer-encrypt-log-spring-boot-starter

引用模块用于外部引用。只有pom.xml文件

  • 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.tuwer</groupId>
    <artifactId>tuwer-encrypt-log-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <description>api结果加密/接口日志starter</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!-- 编译编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- 自动配置模块 -->
        <dependency>
            <groupId>com.tuwer</groupId>
            <artifactId>tuwer-encrypt-log-spring-boot-starter-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

3、自动配置模块

名称:tuwer-encrypt-log-spring-boot-starter-autoconfigure

1> 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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tuwer</groupId>
    <artifactId>tuwer-encrypt-log-spring-boot-starter-autoconfigure</artifactId>
    <version>1.0-SNAPSHOT</version>
    <description>api结果加密/接口日志starter自动配置模块</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!-- 编译编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- 基础启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <!-- 加密工具包 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-crypto</artifactId>
            <version>5.8.16</version>
        </dependency>
    </dependencies>
</project>

2> 自定义注解 Encrypt

package com.tuwer.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <p>加密注解</p>
 *
 * @author 土味儿
 * Date 2023/4/18
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Encrypt {
    String desc() default "";
}

3> 加密工具类

package com.tuwer.util;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;

/**
 * 加解密工具类
 *
 * @author 土味儿
 * Date 2023/4/8
 * @version 1.0
 */
public class TuwerEncryptAesUtil {
    private static String key;

    public static String getKey() {
        // 超过16位时,截取
        if (TuwerEncryptAesUtil.key.length() > 16) {
            TuwerEncryptAesUtil.key = TuwerEncryptAesUtil.key.substring(0, 16);
        }
        // 少于16位时,补全
        if (TuwerEncryptAesUtil.key.length() < 16) {
            int n = 16 - TuwerEncryptAesUtil.key.length();
            StringBuilder _s = new StringBuilder();
            for (int i = 0; i < n; i++) {
                _s.append("-");
            }
            TuwerEncryptAesUtil.key = TuwerEncryptAesUtil.key + _s;
        }

        return key;
    }

    public static void setKey(String key) {
        TuwerEncryptAesUtil.key = key;
    }

    /**
     * 获取AES对象
     *
     * @return
     */
    private static SymmetricCrypto getAes() {
        // 生成密钥
        byte[] byteKey = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), getKey().getBytes()).getEncoded();

        SymmetricCrypto aes = SecureUtil.aes(byteKey);

        return aes;
    }

    /**
     * 加密
     *
     * @param content
     * @return 返回null时,加密失败
     */
    public static String encrypt(String content) {
        try {
            return getAes().encryptBase64(content);
        } catch (Exception e) {
            // 加密失败
            //e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密
     *
     * @param encryptData
     * @return 返回null时,解密失败
     */
    public static String decrypt(String encryptData) {
        try {
            return getAes().decryptStr(encryptData);
        } catch (Exception e) {
            // 解密失败
            //e.printStackTrace();
            return null;
        }
    }
}

4> 密钥属性类

用于注入外部配置的密钥;

对称加密:加密和解密使用同一个密钥;即:请求方和服务方共用密钥。统一在外部配置。

package com.tuwer.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * <p>对称加密属性</p>
 *
 * @author 土味儿
 * Date 2023/4/18
 * @version 1.0
 */
@Data
@ConfigurationProperties(prefix = "encrypt")
public class AesProperty {
    /**
     * 对称加密的密钥
     */
    private String aesSecretKey;
}

5> 加密切面类

拦截api方法,对结果进行加密。

只对有@Encrypt注解的方法进行拦截。拦截后判断方法的返回类型,只有String类型时才加密。

package com.tuwer.aop;

import com.tuwer.util.TuwerEncryptAesUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.util.Objects;

/**
 * <p>加密切面类</p>
 *
 * @author 土味儿
 * Date 2023/4/18
 * @version 1.0
 */
@Aspect
@Slf4j
public class EncryptAspect {
    /**
     * 切入点
     */
    @Pointcut(value = "@annotation(com.tuwer.annotation.Encrypt)")
    private void pointCut(){ }

    /**
     * 加密增强方法
     * 只拦载有 @Encrypt 注解的方法
     * 方法返回类型是字符串时加密
     *
     * @param pj
     * @return
     * @throws Throwable
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pj) throws Throwable {
        log.info("\n");
        log.info("--------------- 加密拦截器 -----------------");
        // 方法签名:String com.tuwer.api...xxx(Integer)
        Signature signature = pj.getSignature();
        log.info("拦截到方法【{}】,准备对结果加密...", signature.toShortString());

        // 执行目标方法proceed
        Object result = pj.proceed();

        // 方法返回类型
        String signatureStr = signature.toString();
        String resultType = signatureStr.substring(0, signatureStr.indexOf(" ")).trim();
        String strModel = "string";
        if (strModel.equalsIgnoreCase(resultType)) {
            // 返回类型是字符串;加密
            log.info("加密中...");
            String encryptResult = TuwerEncryptAesUtil.encrypt(result.toString());
            if (Objects.isNull(encryptResult)) {
                log.info("加密失败!明文返回!");
                log.info("\n");
                return result;
            }
            log.info("已加密!");
            log.info("\n");
            return encryptResult;
        }

        log.info("方法的返回类型不是字符串!不用加密!");
        log.info("\n");
        return result;
    }
}

6> 日志切面类

对所有api接口方法增加日志功能

package com.tuwer.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * <p>日志切面类</p>
 *
 * @author 土味儿
 * Date 2023/4/18
 * @version 1.0
 */
@Aspect
@Slf4j
public class LogAspect {
    /**
     * 切入点
     */
    @Pointcut(value = "execution(* com.tuwer.controller..*(..)) || execution(* com.tuwer.api..*(..))")
    //@Pointcut("${配置文件中key}")
    private void pointCut(){ }
    /**
     * 日志增强方法
     * @param pj
     * @return
     * @throws Throwable
     */
    //@Around("execution(* com.tuwer.controller..*(..))")
    //@Around(value = "execution(* com.tuwer.controller..*(..)) || execution(* com.tuwer.api..*(..))")
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pj) throws Throwable {
        // 当前时间
        long start = System.currentTimeMillis();

        // 执行目标方法proceed
        Object result = pj.proceed();

        // 执行时间
        long end = System.currentTimeMillis();
        long dur = end - start;

        // 输出日志
        log.info("\n");
        log.info("【日志拦载器】:执行方法【{}】,耗时: {}", pj.getSignature().toShortString(), dur);
        log.info("\n");

        return result;
    }
}

7> 自动配置类

package com.tuwer.config;

import com.tuwer.aop.EncryptAspect;
import com.tuwer.aop.LogAspect;
import com.tuwer.util.TuwerEncryptAesUtil;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * <p>自动配置类</p>
 *
 * @author 土味儿
 * Date 2023/4/18
 * @version 1.0
 */
@Configuration
@EnableConfigurationProperties(AesProperty.class)
public class TuwerEncryptLogAutoConfiguration {
    /**
     * 注入 AesProperty 属性配置类
     */
    @Resource
    private AesProperty aesProperty;

    /**
     * 初始化
     */
    @PostConstruct
    private void init(){
        // 给加密工具类注入密钥
        TuwerEncryptAesUtil.setKey(aesProperty.getAesSecretKey());
    }

    /**
     * 加密切面类
     * @return
     */
    @Bean
    public EncryptAspect encryptAspect(){
        return new EncryptAspect();
    }

    /**
     * 日志切面类
     * @return
     */
    @Bean
    public LogAspect logAspect(){
        return new LogAspect();
    }
}

8> spring.factories

指明自动配置类的地址,在 resources 目录下编写一个自己的 META-INF\spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.tuwer.config.TuwerEncryptLogAutoConfiguration

9> install

把starter安装install到本地maven仓库中

三、使用starter

1、引入依赖

<!-- 加密与日志starter -->
<dependency>
	<groupId>com.tuwer</groupId>
	<artifactId>tuwer-encrypt-log-spring-boot-starter</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>

2、配置密钥

application.yml 中添加密钥;16位:超出将截取,不足将补齐

# 密钥
encrypt:
  aes-secret-key: 0123456789123456

3、添加加密注解

  • 在需要加密的api接口上添加 @Encrypt 即可。
  • 关于解密:请求方收到服务方返回的密文后需要先解密。如何自动判断是明文还是密文?明文和密文有明显的区别,通过二者的区别,可以判断是否加密了:
    • 明文:就是Result对象的json字符串。可以直接看到Result对象的code等特征信息。
    • 密文:表面上看就是一串随机的字符,看不到Result对象的特征信息。

在这里插入图片描述

4、日志功能

关于日志功能,只要引入了依赖后,自动生效,不需要额外的配置。

默认的切入点:com.tuwer.controllercom.tuwer.api 包下的所有方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/442580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【虚拟仿真】Unity3D打包WEBGL后播放视频(VideoPlayer组件)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 本篇文章实现Unity3D打包WEBGL后播放视频&#xff0c;如下图所…

实现服务器版本的表白墙

目录 初始前端代码 网页初始效果 一、确定接口 二、编写代码 2.1 创建项目七步走 1、创建Maven项目 2、引入依赖 3、构建目录 4、编写代码 5、打包、部署 ​编辑 7、验证代码 三、具体的代码逻辑 3.1 服务器——两个服务接口 3.2 前端页面的代码 3.2.1 前端存档…

Springcloud快速复习

按照个人的习性,分布式我学习完以后一定会忘为此写次笔记自己快速复习 目录 Springcloud介绍注册中心 Springcloud介绍及微服务介绍 为什么学? 也不是以前的单体架构被淘汰而是,当业务足够大型,进行优化 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff…

SystemUI流程

目录 SystemUI类图 SystemUI流程一&#xff1a;SystemUI启动流程 SystemUI流程二&#xff1a;StatusBar创建流程 SystemUI流程三&#xff1a;系统Notification实现流程 源码基于 Android 12。 SystemUI 是 Android 的系统界面&#xff0c;在 Andorid 系统源码中&#xff0c;…

MySQL安装文档

一、下载 点开下面的链接&#xff1a;https://dev.mysql.com/downloads/mysql/ 点击Download 就可以下载对应的安装包了, 安装包如下: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q0gE9C3W-1682005377951)(assets/image-20221020012428839.png)]…

5. VBA消息框(MsgBox)

MsgBox函数显示一个消息框&#xff0c;并等待用户点击一个按钮&#xff0c;然后根据用户点击的按钮执行相关的操作。 5.1 语法 MsgBox(prompt[,buttons][,title][,helpfile,context]) 5.2 参数说明 prompt - 必需的参数。在对话框中显示为消息的字符串。提示的最大长度大约为…

腾讯云4核8G服务器CVM S5和轻量应用服务器性能PK来了

腾讯云4核8G云服务器可以选择轻量应用服务器或CVM云服务器标准型S5实例&#xff0c;轻量4核8G12M服务器446元一年&#xff0c;CVM S5云服务器935元一年&#xff0c;相对于云服务器CVM&#xff0c;轻量应用服务器性价比更高&#xff0c;轻量服务器CPU和CVM有区别吗&#xff1f;性…

ERROR [io.undertow.request] UT005023: Exception handling request 报错处理

1.背景 找了好久&#xff0c;突然灵光乍现是不是因为容器错误问题&#xff0c;本来就是刚从github上下载下来的项目&#xff0c;怎么别人不报错就我报错嘞&#xff0c;我还什么都没改怎么能这样&#xff01;&#xff01;&#xff01;&#xff01; 这就是我的报错&#xff0c;乍…

storm proxies代理服务器IP如何提高安全性?

代理服务器IP可以通过以下方式来提高安全性&#xff1a; 选择可靠的代理提供商&#xff1a;选择经过信誉验证、有良好用户评价的代理提供商&#xff0c;确保代理服务器IP的稳定性和可靠性&#xff0c;减少被黑客攻击的风险。使用加密通信协议&#xff1a;使用支持加密通信协议&…

【网络编程】网络套接字,UDP,TCP套接字编程

前言 小亭子正在努力的学习编程&#xff0c;接下来将开启javaEE的学习~~ 分享的文章都是学习的笔记和感悟&#xff0c;如有不妥之处希望大佬们批评指正~~ 同时如果本文对你有帮助的话&#xff0c;烦请点赞关注支持一波, 感激不尽~~ 特别说明&#xff1a;本文分享的代码运行结果…

RSA大数N分解Pollard_rho和素数测试Tkinter GUI

RSA大数N分解Pollard_rho和素数测试 系统介绍 : 环境要求&#xff1a; 1、python 2、Tkinter GUI 3、rsa RSA大数N分解和素数测试是密码学中非常重要的问题。其中&#xff0c;RSA算法是基于大质数分解的困难性而设计的公钥加密算法&#xff0c;而素数测试则是判断一个数是…

【0190】Unix 域套接字实战(2)

文章目录 1. UNIX套接字通信2. 为什么需要套接字3. 套接字生命周期4. 示例代码1. UNIX套接字通信 套接字提供进程之间的一种通信方式,即它们交换数据的一种方式。它通常的工作方式是process_a has socket_x, process_b has socket_y, 和两个套接字相连。然后每个进程都可以使…

【Java 数据结构】优先级队列 (堆)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

达梦数据库(DM)的安装教程分享

国产数据库现状 关系型数据库 Oracle 21c 银行、电力、运营商&#xff0c;9i&#xff0c;11g Sqlserver 微软&#xff0c;政府 Mysql 开源 分社区版和商业版 社区版免费 PostgreSQL 开源、国产数据二次开发、学术研究 informix Sybase ERP 行业 DB2 邮政、烟草 国内 武汉达梦(自…

前端面试大全全全全

SEO SEO是搜索引擎优化&#xff08;Search Engine Optimization&#xff09;的缩写&#xff0c;它是指通过对网站的优化来提高其在搜索引擎排名中的位置&#xff0c;从而获得更多的有机流量和更好的网站可见度。 SEO主要包括优化网站的内容、结构、代码等方面&#xff0c;使其…

ES6 块级作用域

ES6之前没有块级作用域&#xff0c;ES5的var没有块级作用域的概念&#xff0c;只有function有作用域的概念&#xff0c;ES6的let、const引入了块级作用域。 ​ ES5之前if和for都没有作用域&#xff0c;所以很多时候需要使用function的作用域&#xff0c;比如闭包。 1.1.1 什么…

基于R语言经典地理加权回归,半参数地理加权回归、多尺度地理加权回归、地理加权主成分分析、地理加权判别分析等空间异质性数据分析

目录 专题一 地理加权回归下的描述性统计学 专题二 地理加权主成分分析 专题三 地理加权回归 专题四 高级回归与回归之外 更多推荐 以地理加权回归为基础的一系列方法&#xff1a;经典地理加权回归&#xff0c;半参数地理加权回归、多尺度地理加权回归、地理加权主成分分析…

从个人角度看什么是加密算法

什么是加密&#xff1f;从程序的角度看&#xff0c;加密就是一个函数&#xff0c;它接收明文P和密钥K作为参数&#xff0c;传入加密函数运算后&#xff0c;得到的返回值&#xff0c;称之为密文C C encrypt(P, K);而解密&#xff0c;就是对加密的逆操作。把密文C和密钥K作为参…

102. 二叉树的层序遍历【206】

难度等级&#xff1a;中等 上一篇算法&#xff1a; 215. 数组中的第K个最大元素【382】 力扣此题地址&#xff1a; 102. 二叉树的层序遍历 - 力扣&#xff08;Leetcode&#xff09; 1.题目&#xff1a;102. 二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值…

构建清晰、高效的Android应用程序:了解Android架构组件

概述 Android 架构组件是一个由 Google 推出的集成库&#xff0c;旨在使 Android 应用开发更加快捷、高效和易于维护。Android 架构组件提供了一套可扩展的 API&#xff0c;帮助开发者在编写 Android 应用时&#xff0c;更好地组织应用的代码&#xff0c;并提供了一些通用的、…