一个注解实现SpringBoot接口请求数据和返回数据加密,提高系统安全性!

news2025/2/23 10:36:43

注解实现接口加密

  • 1、前言
    • 1.1、前端必看
    • 1.2、后端必看
  • 2、后端注解实现
    • 2.1、实现流程
    • 2.2、开始实现
      • 2.2.1、 pom
      • 2.2.2、 注解
      • 2.2.3、 加密工具类
      • 2.2.3、 定义切面(注意切点包名)
      • 2.2.4、 定义加密基类与各种入参VO
      • 2.2.5、写两个Controller
  • 3、参考文章

1、前言

  起因是公司给人开发的内网系统登录接口采用明文传输,本来用着没啥问题(其实不太好!),毕竟只是内网使用。后面需要外网访问,需要提高安全等级,禁止密码明文传输。于是就有了这篇文章。本文采用:对称加密
有问题联系Qq:1101165230

1.1、前端必看

  需要说明的是,加密传输是需要前端配合的,毕竟请求接口的是前端。所以前端也需要知道加密模式、填充方式与偏移量等基础知识。同时需要商定是对称加密还是非对称加密。

  前端加密快速上手

1.2、后端必看

点开链接:按照目录浏览

  后端加密快速上手

2、后端注解实现

  好的,你已经了解了上面的基础知识,接下来你准备好为你项目编写一个牛x的功能了吗?

2.1、实现流程

实现流程描述

2.2、开始实现

开始前我们现在application.yml 中增加一个配置

# 配置是否开启AOP参数加密解密,不配置默认为true
MySecret:
  isSecret: true 

2.2.1、 pom

	<!-- AOP切面依赖 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
	</dependency>
	
	<!-- lombok工具 -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>
	
	<!-- json操作类 -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>1.2.52.sec06</version>
	</dependency>

	 <!-- String工具包 -->
	<dependency>
		<groupId>org.apache.commons</groupId>
		 <artifactId>commons-lang3</artifactId>
	</dependency>

2.2.2、 注解

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

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secret {

    Class value();

    // 参数类中传递加密数据的属性名,默认encryptStr
    String encryptStrName() default "encryptStr";
}

2.2.3、 加密工具类

  注意AES_KEY需要更改,切记和前端商量

import com.alibaba.fastjson.JSON;
import com.example.undertwo.entity.vo.UserVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author by Guoshun
 * @version 1.0.0
 * @description 支持AES、DES、RSA加密、数字签名以及生成对称密钥和非对称密钥对
 * @date 2024/5/8 15:04
 */
public class CryptoUtils {

    public static final String AES_KEY = "IQcoqkg==";
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    private static final Encoder BASE64_ENCODER = Base64.getEncoder();
    private static final Decoder BASE64_DECODER = Base64.getDecoder();
    private static final Map<Algorithm, KeyFactory> KEY_FACTORY_CACHE = new ConcurrentHashMap<>();
    private static final Map<Algorithm, Cipher> CIPHER_CACHE = new HashMap<>();

    /**
     * 对称加密
     *
     * @param secretKey 密钥
     * @param iv        加密向量,只有CBC模式才支持,如果是CBC则必传
     * @param plainText 明文
     * @param algorithm 对称加密算法,如AES、DES
     * @return
     * @throws Exception
     */
    public static String encryptSymmetrically(String secretKey, String iv, String plainText, Algorithm algorithm) throws Exception {
        SecretKey key = decodeSymmetricKey(secretKey, algorithm);
        IvParameterSpec ivParameterSpec = StringUtils.isBlank(iv) ? null : decodeIv(iv);
        byte[] plainTextInBytes = plainText.getBytes(DEFAULT_CHARSET);
        byte[] ciphertextInBytes = transform(algorithm, Cipher.ENCRYPT_MODE, key, ivParameterSpec, plainTextInBytes);

        return BASE64_ENCODER.encodeToString(ciphertextInBytes);
    }

    /**
     * 对称解密
     *
     * @param secretKey  密钥
     * @param iv         加密向量,只有CBC模式才支持,如果是CBC则必传
     * @param ciphertext 密文
     * @param algorithm  对称加密算法,如AES、DES
     * @return
     * @throws Exception
     */
    public static String decryptSymmetrically(String secretKey, String iv, String ciphertext, Algorithm algorithm) throws Exception {
        SecretKey key = decodeSymmetricKey(secretKey, algorithm);
        IvParameterSpec ivParameterSpec = StringUtils.isBlank(iv) ? null : decodeIv(iv);
        byte[] ciphertextInBytes = BASE64_DECODER.decode(ciphertext);
        byte[] plainTextInBytes = transform(algorithm, Cipher.DECRYPT_MODE, key, ivParameterSpec, ciphertextInBytes);
        return new String(plainTextInBytes, DEFAULT_CHARSET);
    }

    /**
     * 将密钥进行Base64位解码,重新生成SecretKey实例
     *
     * @param secretKey 密钥
     * @param algorithm 算法
     * @return
     */
    private static SecretKey decodeSymmetricKey(String secretKey, Algorithm algorithm) {
        byte[] key = BASE64_DECODER.decode(secretKey);
        return new SecretKeySpec(key, algorithm.getName());
    }

    private static IvParameterSpec decodeIv(String iv) {
        byte[] ivInBytes = BASE64_DECODER.decode(iv);
        return new IvParameterSpec(ivInBytes);
    }

    private static byte[] transform(Algorithm algorithm, int mode, Key key, IvParameterSpec iv, byte[] msg) throws Exception {
        Cipher cipher = CIPHER_CACHE.get(algorithm);
        // double check,减少上下文切换
        if (cipher == null) {
            synchronized (CryptoUtils.class) {
                if ((cipher = CIPHER_CACHE.get(algorithm)) == null) {
                    cipher = determineWhichCipherToUse(algorithm);
                    CIPHER_CACHE.put(algorithm, cipher);
                }
                cipher.init(mode, key, iv);
                return cipher.doFinal(msg);
            }
        }

        synchronized (CryptoUtils.class) {
            cipher.init(mode, key, iv);
            return cipher.doFinal(msg);
        }
    }

    private static Cipher determineWhichCipherToUse(Algorithm algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException {
        Cipher cipher;
        String transformation = algorithm.getTransformation();
        // 官方推荐的transformation使用algorithm/mode/padding组合,SunJCE使用ECB作为默认模式,使用PKCS5Padding作为默认填充
        if (StringUtils.isNotEmpty(transformation)) {
            cipher = Cipher.getInstance(transformation);
        } else {
            cipher = Cipher.getInstance(algorithm.getName());
        }

        return cipher;
    }

    /**
     * 算法分为加密算法和签名算法,更多算法实现见:<br/>
     * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">jdk8中的标准算法</a>
     */
    public static class Algorithm {

        @Getter
        private String name;
        @Getter
        private String transformation;
        @Getter
        private int keySize;

        public Algorithm(String name, int keySize) {
            this(name, null, keySize);
        }

        public Algorithm(String name, String transformation, int keySize) {
            this.name = name;
            this.transformation = transformation;
            this.keySize = keySize;
        }

        public interface Encryption {
            Algorithm AES_ECB_PKCS5 = new Algorithm("AES", "AES/ECB/PKCS5Padding", 128);
            Algorithm AES_CBC_PKCS5 = new Algorithm("AES", "AES/CBC/PKCS5Padding", 128);
            Algorithm DES_ECB_PKCS5 = new Algorithm("DES", "DES/ECB/PKCS5Padding", 56);
            Algorithm DES_CBC_PKCS5 = new Algorithm("DES", "DES/CBC/PKCS5Padding", 56);
            Algorithm RSA_ECB_PKCS1 = new Algorithm("RSA", "RSA/ECB/PKCS1Padding", 1024);
            Algorithm DSA = new Algorithm("DSA", 1024);
        }

        public interface Signing {
            Algorithm SHA1WithDSA = new Algorithm("SHA1withDSA", 1024);
            Algorithm SHA1WithRSA = new Algorithm("SHA1WithRSA", 2048);
            Algorithm SHA256WithRSA = new Algorithm("SHA256WithRSA", 2048);
        }

    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class AsymmetricKeyPair {

        private String publicKey;
        private String privateKey;
    }

    /**
     * 加密
     * @param jsonStr
     * @return
     * @throws Exception
     */
    public static String encrypt(String jsonStr) throws Exception {
       return CryptoUtils.encryptSymmetrically(AES_KEY, null, jsonStr, Algorithm.Encryption.AES_ECB_PKCS5);
    }

    /**
     * 解密
     * @param jsonStr
     * @return
     * @throws Exception
     */
    public static String decrypt(String jsonStr) throws Exception {
       return CryptoUtils.decryptSymmetrically(AES_KEY, null, jsonStr, Algorithm.Encryption.AES_ECB_PKCS5);
    }

    public static void main(String[] args) throws Exception {
        UserVO userVO = new UserVO();
        userVO.setId(123);
        userVO.setName("阿萨");
        String encrypt = encrypt(JSON.toJSONString(userVO));
        System.out.println("加密后:" + encrypt);
        String decrypt = decrypt(encrypt);
        UserVO userVO1 = JSON.parseObject(decrypt, UserVO.class);
        System.out.println("解密后" + userVO1.toString());

    }

2.2.3、 定义切面(注意切点包名)

import com.alibaba.fastjson.JSON;
import com.example.undertwo.config.Secret;
import com.example.undertwo.entity.result.ResultVO;
import com.example.undertwo.utils.CryptoUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @author by Guoshun
 * @version 1.0.0
 * @description 切面加密解密
 * @date 2024/5/9 14:14
 */
@Aspect
@Component
@Slf4j
public class SecretAOP {

    // 是否进行加密解密,通过配置文件注入(不配置默认为true)
    @Value("${MySecret.isSecret:true}")
    boolean isSecret;

    // 定义切点,使用了@Secret注解的类 或 使用了@Secret注解的方法
    @Pointcut("@within(com.example.undertwo.config.Secret) || @annotation(com.example.undertwo.config.Secret)")
    public void pointcut(){}

    // 环绕切面
    @Around("pointcut()")
    public ResultVO around(ProceedingJoinPoint point){
        ResultVO result = null;
        // 获取被代理方法参数
        Object[] args = point.getArgs();
        // 获取被代理对象
        Object target = point.getTarget();
        // 获取通知签名
        MethodSignature signature = (MethodSignature )point.getSignature();

        try {
            // 获取被代理方法
            Method pointMethod = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
            // 获取被代理方法上面的注解@Secret
            Secret secret = pointMethod.getAnnotation(Secret.class);
            // 被代理方法上没有,则说明@Secret注解在被代理类上
            if(secret==null){
                secret = target.getClass().getAnnotation(Secret.class);
            }

            if(secret!=null){
                // 获取注解上声明的加解密类
                Class clazz = secret.value();
                // 获取注解上声明的加密参数名
                String encryptStrName = secret.encryptStrName();

                for (int i = 0; i < args.length; i++) {
                    // 如果是clazz类型则说明使用了加密字符串encryptStr传递的加密参数
                    if(clazz.isInstance(args[i])){
                        Object cast = clazz.cast(args[i]);      //将args[i]转换为clazz表示的类对象
                        // 通过反射,执行getEncryptStr()方法,获取加密数据
                        Method method = clazz.getMethod(getMethedName(encryptStrName));
                        // 执行方法,获取加密数据
                        String encryptStr = (String) method.invoke(cast);
                        // 加密字符串是否为空
                        if(StringUtils.isNotBlank(encryptStr)){
                            // 解密
                            String json = CryptoUtils.decrypt(encryptStr);
                            // 转换vo
                            args[i] = JSON.parseObject(json, (Type) args[i].getClass());
                        }
                    }
                    // 其他类型,比如基本数据类型、包装类型就不使用加密解密了
                }
            }

            // 执行请求
            result = (ResultVO) point.proceed(args);

            // 判断配置是否需要返回加密
            if(isSecret){
                // 获取返回值json字符串
                String jsonString = JSON.toJSONString(result.getData());
                // 加密
                String s = CryptoUtils.encrypt(jsonString);
                result.setData(s);
            }

        } catch (NoSuchMethodException e) {
            log.error("@Secret注解指定的类没有字段:encryptStr,或encryptStrName参数字段不存在");
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    // 转化方法名
    private String getMethedName(String name){
        String first = name.substring(0,1);
        String last = name.substring(1);
        first = StringUtils.upperCase(first);
        return "get" + first + last;
    }
}

2.2.4、 定义加密基类与各种入参VO

import lombok.Data;

/**
 * @author by Guoshun
 * @version 1.0.0
 * @description 基础的实体类
 * @date 2024/5/9 14:00
 */
@Data
public class BaseVO {
    private String encryptStr;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author by Guoshun
 * @version 1.0.0
 * @description 部门类
 * @date 2024/5/9 14:16
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptVO{

    private Integer id;

    private String deptName;

    // 自己实现的一个参数,用来给前端传递加密字符串
    private String encryptJson;

}
import com.example.undertwo.entity.BaseVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author by Guoshun
 * @version 1.0.0
 * @description TODO
 * @date 2024/5/9 14:05
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVO extends BaseVO {

    private Integer id;

    private String name;

}

2.2.5、写两个Controller


import com.example.undertwo.config.Secret;
import com.example.undertwo.entity.result.ResultVO;
import com.example.undertwo.entity.vo.DeptVO;
import org.springframework.web.bind.annotation.*;


/**
 * @author by Guoshun
 * @version 1.0.0
 * @description 部门类
 * @date 2024/5/9 14:17
 */
@RestController
@RequestMapping("dept")
public class DeptController {

    @GetMapping("getDeptName/{id}")
    public ResultVO getDeptName(@PathVariable("id") String id){
        return new ResultVO(0,"查询成功","财务部" + id);
    }

    // 注解在方法上,并传递了encryptStrName自己定义的加密字符串名称encryptJson
    @Secret(value = DeptVO.class,encryptStrName = "encryptJson")
    @PostMapping("addDept")
    public ResultVO addDept(@RequestBody DeptVO dept){
        return new ResultVO(0,"新增成功",dept);
    }

}

import com.example.undertwo.config.Secret;
import com.example.undertwo.entity.BaseVO;
import com.example.undertwo.entity.result.ResultVO;
import com.example.undertwo.entity.vo.UserVO;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


/**
 * @author by Guoshun
 * @version 1.0.0
 * @description  用户控制类
 * @date 2024/5/9 14:10
 */
@Secret(BaseVO.class)                             //接口参数和返回要进行加解密
@RestController
@RequestMapping("user")
public class UserController {

    //采用内部类的实例代码块方式初始化map
    HashMap<Integer, UserVO> userMap = new HashMap<Integer, UserVO>(){
        {
            put(1,new UserVO(1,"张三"));
            put(2,new UserVO(2,"李四"));
            put(3,new UserVO(3,"王五"));
        }
    };

    // 通过id查询用户
    @GetMapping("getUserName/{id}")
    public ResultVO getUserName(@PathVariable("id")  Integer id){
        return new ResultVO(0,"查询成功",userMap.get(id));
    }

    // 通过name查询用户id
    @GetMapping("getUserId")
    public ResultVO getUserId(@RequestParam  String name){
        Iterator<Map.Entry<Integer, UserVO>> iterator = userMap.entrySet().iterator();
        UserVO u = null;
        while (iterator.hasNext()){
            Map.Entry<Integer, UserVO> entry = iterator.next();
            if(entry.getValue().getName().equals(name)){
                u = entry.getValue();
                break;
            }
        }
        return new ResultVO(0,"查询成功",u);
    }

    // 新增用户
    @PostMapping("addUser")
    public ResultVO addUser(@RequestBody UserVO user){
        return new ResultVO(0,"新增成功",user);
    }

    // 更改用户
    @PostMapping("updateUser")
    public ResultVO updateUser(@RequestBody UserVO user) throws Throwable {
        if(user==null||user.getId()==null){
            throw new NullPointerException();
        }else{
            return new ResultVO(0,"修改成功",user);
        }
    }
}

3、参考文章

[1]:springboot 自定义注解使用AOP实现请求参数解密以及响应数据加密
[2]:前端CryptoJS和Java后端数据互相加解密(AES)

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

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

相关文章

深圳盐田某前沿研究所:OLED透明屏引领未来科技空间

产品&#xff1a;55寸OLED透明屏 项目时间&#xff1a;2024年04月 项目地点&#xff1a;深圳盐田 在科技日新月异的今天&#xff0c;前沿的研究机构不仅追求科研的突破&#xff0c;也在不断探索和尝试将最新科技融入其工作环境之中。深圳盐田的一家前沿研究所便是这一探索的先…

搜维尔科技:Xsens惯性动捕+Manus手套用于游戏开发制作

用户名称&#xff1a;北京源力星聚网络科技有限公司&#xff08;Bilibili控股子公司&#xff09; 主要产品&#xff1a;Xsens MVN link *2 &#xff0c;Manus手套*2 &#xff0c;MVN Animate Pro软件等 使用培训现场 使用2套Xsens MVN link 2副Manus手套Xsens Animate Pro软件…

成都数字产业园深度链接校企双方,共建产学研合作研发中心

在数字经济的浪潮中&#xff0c;企业与学院之间的深度链接显得尤为关键。作为人才输送的源头&#xff0c;学院承载着为社会培育高素质人才的使命&#xff1b;而作为创新与实践的基地&#xff0c;企业则渴望吸引这些新鲜血液&#xff0c;为自身的持续发展注入新的活力。近日&…

Adobe使用常见问题解答,如何续费?

“Adobe系统状态”页上的颜色表示什么&#xff1f; Gray表示我们调查了潜在的服务中断并确定它没有影响。橙色表示轻微问题。红色表示严重问题。蓝色表示定期维护。绿色表示所有服务均运行正常。 如何查看以前的CSO或CMR&#xff1f; 导航到 https://status.adobe.com.单击所…

超声波清洗机哪个品牌好用?热心推荐四款全能硬核的超声波清洗机

要知道&#xff0c;超声波清洗机的出现&#xff0c;对于那些经常戴眼镜的人来说&#xff0c;无疑是一种让他们解放了双手的清洁工具&#xff0c;由于戴着眼镜久了&#xff0c;就算用水冲洗&#xff0c;也不能够清洁到镜片缝隙里的脏污渍&#xff0c;但是随着时间的推移&#xf…

SAP-CentralFinance - 学习心得3 - 应付账款与物料管理之间的集成

业务示例 工厂 1010 需要轮胎和内胎。负责的采购组织将相应的采购订单交给了某知名供应商。数天之后&#xff0c;先收到了货物&#xff0c;然后收到了发票。 物料管理中的组织单位 物流的中央组织对象为工厂。工厂是公司的运营范围或分支。工厂可以是集中交货仓库、地区销售…

(五)STM32F407 cubemx IIC驱动OLED(3)软件篇

这篇文章主要是个人的学习经验&#xff0c;想分享出来供大家提供思路&#xff0c;如果其中有不足之处请批评指正哈。   废话不多说直接开始主题&#xff0c;本人是基于STM32F407VET6芯片&#xff0c;但是意在你看懂这篇文章后&#xff0c;不管是F1,F4,H7等一系列系统硬件IIC配…

Elastic 基于 RAG 的 AI 助手:利用 LLM 和私有 GitHub 问题分析应用程序问题

作者&#xff1a;来自 Elastic Bahubali Shetti 作为 SRE&#xff0c;分析应用程序比以往更加复杂。 你不仅必须确保应用程序以最佳状态运行以确保良好的客户体验&#xff0c;而且还必须了解某些情况下的内部工作原理以帮助排除故障。 分析基于生产的服务中的问题是一项团队运动…

迷宫中离入口最近的出口

题目链接 迷宫中离入口最近的出口 题目描述 注意点 maze[i][j] 要么是 ‘.’ &#xff0c;要么是 ‘’entrance.length 2entrance 一定是空格子出口的含义是 maze 边界上的空格子entrance格子不算出口 解答思路 广度优先遍历找到走i步时所能到达的所有节点位置&#xff0…

趋势交易如何交易?fpmarkets实例分享

通过之前的文章&#xff0c;各位聪明的投资者想必都已经知道什么是趋势交易了 &#xff01;但是趋势交易如何交易呢&#xff1f;其实很简单&#xff0c;只要理解了趋势交易的信号点以及如何获取的信号就能 很快的掌握如何使用趋势交易进行盈利&#xff0c;下面fpmarkets澳福就和…

实验过程演示【计算机网络实验】

前言 这是陈旧已久的草稿2023-05-20 11:23:54 这个是计算机网络的一个实验&#xff0c;现在也不知道这个是啥来着。 现在2024-5-12 22:33:17&#xff0c;发布到[计算机网络实验]专栏中。 实验过程演示 2023-5-18 20:17:45 1&#xff0e;搭建一个多跳网络拓扑&#xff0c;…

ViewModel 完全指南:实践与背后原理全解

一、引言 在现代Android应用开发中&#xff0c;处理UI数据的有效管理和状态保持是开发者面临的重要挑战之一。Google推出的Jetpack组件库中的ViewModel已成为解决这些问题的关键工具。ViewModel旨在以生命周期意识的方式存储和管理界面相关的数据&#xff0c;从而使数据在配置…

腐烂的橘子BFS

题目&#xff1a; 腐烂的橘子 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b; 值 1 代表新鲜橘子&#xff1b; 值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子…

探索大语言模型代理(Agent):研究背景、通用框架与未来展望

引言 近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;在智能代理&#xff08;Agent&#xff09;领域中的应用已成为研究的热点。这些代理不仅能够模拟人类的认知过程&#xff0c;还能在复杂的社会环…

JPA@Entry报错Could not determine recommended JdbcType for Java type

问题很明显&#xff0c;无法自动决定类型&#xff0c;那就手动告诉该字段。 一、直接上解决方案 如果是一对一的关系用 OneToOne 如果是一对多的关系用 OneToMany 如果是多对一的关系用 ManyToOne 二、另一个无空构造函数的问题 使用注解后&#xff0c;注解报错找不到空的…

奇舞周刊第527期:​Virtual DOM(虚拟DOM) 的地位再一次被挑战 !!!

奇舞推荐 ■ ■ ■ Virtual DOM&#xff08;虚拟DOM&#xff09; 的地位再一次被挑战 &#xff01;&#xff01;&#xff01; 最近在前端圈有一个 Github Repo 算是蛮受关注的 - proposal-signals&#xff0c;这是一个由 Daniel Ehrenberg 为主导&#xff0c;向 TC39 提案的项目…

【学习笔记】人群归因分数 PAF 以及combined PAF(更新)

在此推荐2篇发表在lancet以及jama子刊上的paf文章&#xff0c;这两篇文章套路是一样的&#xff0c;只是在不同国家进行。 在计算combined PAF或者说weighted PAF的时候&#xff0c;先建立了相关矩阵&#xff0c;再做主成分分析&#xff0c;得到communality。详细信息大家可翻阅…

C语言之指针初阶

目录 前言 一、内存与地址的关系 二、指针变量 三、野指针 四、const 五、传值调用与传址调用 总结 前言 本文主要介绍C语言指针的一些基础知识&#xff0c;为后面深入理解指针打下基础&#xff0c;因此本文内容主要包括内存与地址的关系&#xff0c;指针的基本语法&…

Pycharm使用Anaconda虚拟环境

一、前置 安装 Pychram安装 Anaconda&#xff0c;并配置虚拟环境 参考&#xff1a; Anaconda虚拟环境 anaconda虚拟环境pytorch安装 二、在Pycharm中使用Anaconda的虚拟环境 打开 Pycharm的命令行可以看到 Anaconda 的虚拟环境已经启动。 三、问题集合 &#xff08;1&…

都是免费的SSL证书,有什么区别

国内做SSL证书的服务商还是比较多&#xff0c;但也不是所有服务商都提供免费的SSL证书&#xff0c;一般只有少数几家提供免费SSL证书。那么&#xff0c;同样都是免费的SSL证书&#xff0c;有哪些不一样的地方呢&#xff1f; 1、验证类型&#xff1a;免费SSL证书通常只提供域名…