Springboot数据加密篇

news2024/11/16 3:32:23

一、密码加密

1.1Hash算法(MD5/SHA-512等)

哈希算法,又称摘要算法(Digest),是一种将任意长度的输入通过散列函数变换成固定长度的输出的单向密码体制。这种映射的规则就是哈希算法,而通过原始数据映射之后得到的二进制值串就是哈希值。

哈希算法最重要的特点就是:相同的输入一定得到相同的输出,不同的输入可能得到相同的输出,但不可能通过输出反推出原始输入。这意味着哈希算法可以用于快速比较两个数据是否相同,常用于密码存储、数字签名、数据完整性校验等领域。

Hash算法特性 

  • 唯一性。数据通过hash算法计算的hash值是唯一的
  • 压缩性。例如,任意长度的数据,算出的MD5值的长度是固定的(128位二进制数,32位十六进制数)
  • 不可逆。无法从结果复原源数据信息
  • 抗修改。对原数据的任何改动,hash值完全不同
  • 强抗碰撞。伪造数据非常困难
  • 容易计算。从原数据计算出值很容易

Hash算法无法转换回源数据,因此是签名算法,不是加密/解密算法(无解密)

即,仅判断是不是源数据,不知道源数据是什么。因此适合,验证敏感源数据的正确性。例如,验证密码(如何判断密码正确?) 

 Hash算法缺点

 1.2加Salt算法

Salt(盐)是在密码学中常用的一种安全措施,其本质是一段随机的字符串。在密码加密过程中,Salt会被添加到原始密码中,再通过散列函数进行散列,最终生成一个唯一的散列值。

Salt的主要作用是增加破解密码的难度,因为即使两个用户使用了相同的密码,由于Salt的存在,他们的密码散列值也会不同。这就意味着,攻击者即使获取了数据库中的密码散列值,也无法通过简单的对比找到匹配的原始密码。

 1.3Spring-Security

Spring提供了一套安全框架,处理加密/解密数据信息 提供了包括对称/非对称加密,不同Hash算法等一系列实现

 相关配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

相关接口

PasswordEncoder(org.springframework.security.crypto.password)接口

PasswordEncoder接口是Spring Security中用于对密码进行加密的接口。它提供了一种通用的方法来将明文密码转换为加密后的密码,以便在存储和验证过程中使用。

  • String encode(CharSequence rawPassword),编码密码
  • boolean matches(CharSequence rawPassword, String encodedPassword),验证原始密码与编码密码
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordEncoderExample {
    public static void main(String[] args) {
        // 创建PasswordEncoder实例
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        // 原始密码
        String plainPassword = "myPassword";

        // 加密密码
        String encodedPassword = passwordEncoder.encode(plainPassword);

        System.out.println("原始密码: " + plainPassword);
        System.out.println("加密后的密码: " + encodedPassword);
    }
}

 Pbkdf2PasswordEncoder类,Pbkdf2算法

 Pbkdf2PasswordEncoder类是Spring Security中的一个密码编码器,它使用PBKDF2算法对密码进行加密。PBKDF2是一种密钥导出函数,它可以从用户输入的密码生成一个足够复杂的密钥,以保护存储在数据库中的密码。

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordEncoderExample {
    public static void main(String[] args) {
        // 创建Pbkdf2PasswordEncoder对象
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        // 原始密码
        String plainPassword = "myPassword";

        // 使用Pbkdf2算法加密密码
        String encodedPassword = passwordEncoder.encode(plainPassword);

        System.out.println("原始密码: " + plainPassword);
        System.out.println("加密后的密码: " + encodedPassword);
    }
}

 BCryptPasswordEncoder类,Bcrypt算法

BCryptPasswordEncoder类是Spring Security中的一个密码编码器,它使用Bcrypt算法对密码进行加密。Bcrypt是一种加密算法,它可以生成一个足够复杂的哈希值来保护存储在数据库中的密码。自动生成随机盐值,并附在结果,避免盐值的单独保存

  • 128bits随机二进制数,16bytes,base64,24chars,特殊算法转为22chars
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordEncoderExample {
    public static void main(String[] args) {
        // 创建BCryptPasswordEncoder对象
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        // 原始密码
        String plainPassword = "myPassword";

        // 使用Bcrypt算法加密密码
        String encodedPassword = passwordEncoder.encode(plainPassword);

        System.out.println("原始密码: " + plainPassword);
        System.out.println("加密后的密码: " + encodedPassword);
    }
}

1.4实现

配置类

package com.passwordencoder;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfiguration {
    // 定义一个名为getPasswordEncoder的方法,返回类型为PasswordEncoder
    @Bean
    public PasswordEncoder getPasswordEncoder() {
        // 创建一个新的BCryptPasswordEncoder对象并返回
        return new BCryptPasswordEncoder();
    }
}

状态类

package com.passwordencoder.vo;

import lombok.Builder;
import lombok.Data;

import java.util.Map;

@Data
@Builder
public class ResultVO {
    private int code;
    private String message;
    private Map<String, Object> data;

    public static ResultVO success(Map<String, Object> data) {
        return ResultVO.builder().code(200).data(data).build();
    }

    public static ResultVO error(int code, String msg) {
        return ResultVO.builder().code(code).message(msg).build();
    }
}

实体类

package com.passwordencoder.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User04 {
    private String userName;
    private String password;
}

服务类

package com.example.springmvcexamples.example04.passwordencoder.service;

import com.example.springmvcexamples.example04.passwordencoder.entity.User04;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j // 使用lombok的注解,简化代码并自动生成getter、setter等方法
@Service // 标记这是一个Spring服务类
public class UserService04 {

    // 根据用户名获取用户信息的方法
    public User04 getUser(String userName) {
        // 如果用户名为"BO",则返回一个包含用户名和加密密码的用户对象
        return "BO".equals(userName)
                ? User04.builder() // 使用User04的构建器模式创建一个新的用户对象
                .userName("BO") // 设置用户名为"BO"
                .password("$2a$10$A7OcKw5xxRMh9c4ghWySr.Rjh22gpWyiWExZO5i2B32eJLQrFXcr6") // 设置加密后的密码
                .build() // 构建并返回用户对象
                : null; // 如果用户名不为"BO",则返回null
    }
}

处理类

package com.passwordencoder.controller;



import com.passwordencoder.entity.User04;
import com.passwordencoder.service.UserService04;
import com.passwordencoder.vo.ResultVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/example04/")
@RequiredArgsConstructor
public class ExampleController04 {

    private final UserService04 userService;
    private final PasswordEncoder passwordEncoder;

    @PostMapping("login")
    public ResultVO login(@RequestBody User04 user) {
        // 先查询用户是否存在
        User04 u = userService.getUser(user.getUserName());
        if (u == null || !passwordEncoder.matches(user.getPassword(), u.getPassword())) {
            log.debug("登录失败");
            return ResultVO.error(401, "用户名密码错误");
        }
        // 登录成功,添加token等操作
        log.debug("登录成功");
        return ResultVO.success(Map.of("user", u));
    }
}

测试

POST http://localhost:8081/api/example04/login
Content-Type: application/json

{
  "userName": "BO",
  "password": "12345"
}

二、序列化与反序列化

  • SpringMVC默认基于Jackson实现序列化/反序列化
  • SpringMVC自动注入Jackson ObjectMapper映射对象到容器
    • String writeValueAsString(T payload),将对象序列化为json字符串
    • T readValue(String content, Class c),将json字符串反序列化为指定类型的Java对象

TypeReference<T>抽象类。创建子类,具体化泛型。可通过创建类似接口的匿名内部类实现

三、token令牌

 3.1概述

Token令牌是一种用于身份验证和授权的凭证,通常由服务器生成并发送给用户。它包含有关用户的信息,例如用户名、角色等,以及一些会话信息,例如过期时间等。当用户尝试访问受保护的资源时,他们需要提供有效的Token令牌以证明其身份和权限。服务器将验证Token令牌的有效性,并根据其中包含的信息授予或拒绝用户的请求。

Restful设计思想,服务器端不再保存用户状态(无HttpSession)

  • 用户登录后,将用户身份/权限信息封装在Token(令牌)
  • ·将token信息加密(Authorization)通过http header返给客户端
  • ·客户端每次需要身份/权限的请求,均需在http header携带Authorization
  • ·服务器端拦截权限请求,从Authorization中解密出Token权鉴

实现

  • ·JWT。流行的认证标准,信息由header/payload/,signature组成,多种实现
  • ·自定义Token。更灵活,数据量小

3.2适合敏感数据的加密传输 

加密/解密算法,适合敏感数据的加密传输

  • 对称加密算法(AES等),通过相同密钥加密/解密
  • 非对称加密算法(RSA等),公钥加密的数据,必须通过私钥才能解密 

密钥生成器

my:
  secretkey: R28K42ZEJ8LWRHU5
  salt: 636eac2534bcfcb0

实体类

package com.example.springmvcexamples.example05;

import lombok.Data;

@Data
public class MyToken {
    public enum Role{
        USER, ADMIN
    }
    private Integer uid;
    private Role role;
}

组件类

package com.example.springmvcexamples.example05.textencryptor;

import com.example.springmvcexamples.example02.handlingexception.exception.MyException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;

@Component
public class EncryptComponent05 {

    private final ObjectMapper objectMapper;
    @Value("${my.secretkey}")
    private String secretKey;
    @Value("${my.salt}")
    private String salt;
    private TextEncryptor encryptor;

    public EncryptComponent05(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /**
     * 直接基于密钥/盐值创建单例TextEncryptor对象。避免反复创建
     */
    @PostConstruct
    public void getTextEncryptor() {
        encryptor = Encryptors.text(secretKey, salt);
    }

    public String encrypt(Map<String, Object> payload) {
        try {
            String json = objectMapper.writeValueAsString(payload);
            return encryptor.encrypt(json);
        } catch (JsonProcessingException e) {
            throw new MyException(500, "服务器端错误");
        }
    }

    /**
     * 无法验证/解密/反序列化,说明数据被篡改,判定无权限
     * @param auth
     * @return
     */
    public Map<String, Object> decrypt(String auth) {
        try {
            String json = encryptor.decrypt(auth);
            return objectMapper.readValue(json, Map.class);
        } catch (Exception e) {
            throw new MyException(403, "无权限");
        }
    }
}

@Component注解

`@Component`注解是Spring框架中的一个注解,用于标记一个类作为Spring容器中的组件。当Spring容器启动时,会自动扫描带有`@Component`注解的类,并将这些类实例化为对象,然后将这些对象存储在Spring容器中,以便在其他组件中通过依赖注入的方式使用。

测试一

package com.example.springmvcexamples.example05;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;

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

@SpringBootTest
@Slf4j
public class EncryptorTest {
    // 自动注入ObjectMapper对象,用于将对象转换为JSON字符串和将JSON字符串转换为对象
    @Autowired
    private ObjectMapper objectMapper;
    // 从配置文件中获取加密密钥
    @Value("${my.secretkey}")
    private String secretKey;
    // 从配置文件中获取盐值
    @Value("${my.salt}")
    private String salt;
    // 测试方法
    @Test
    public void test_encrypt() {
        // 创建加密器,使用密钥和盐值进行加密
        TextEncryptor encryptor = Encryptors.text(secretKey, salt);
        try {
            // 创建一个包含uid和role的Map对象
            Map<String, Object> map = Map.of("uid", 1384896304762638307L, "role", 9);
            // 将Map对象转换为JSON字符串
            String json = objectMapper.writeValueAsString(map);
            // 使用加密器对JSON字符串进行加密
            String r = encryptor.encrypt(json);
            // 输出加密后的字符串
            log.debug(r);
            // 输出加密后的字符串长度
            log.debug("{}", r.length());
            // 再次使用加密器对JSON字符串进行加密,验证加密结果是否一致
            log.debug(encryptor.encrypt(json));

            // 使用加密器对加密后的字符串进行解密
            String reJson = encryptor.decrypt(r);
            // 将解密后的JSON字符串转换为Map对象
            Map<String, Object> reToken = objectMapper.readValue(reJson, Map.class);
            // 输出解密后的Map对象中的role值
            log.debug(reToken.get("role").toString());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

测试二

package com.example.springmvcexamples.example05;

import com.example.springmvcexamples.example05.textencryptor.EncryptComponent05;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Map;

@SpringBootTest
@Slf4j
public class TextEncryptorTest {
    @Autowired
    private EncryptComponent05 encrypt;



    @Test
    public void test_encrypt() {
//        MyToken Token = new MyToken();
//        Token.setUid(1);
//        Token.setRole(MyToken.Role.ADMIN);
        Map<String, Object> map = Map.of("uid", 1384896304762638307L, "role", 9);
        String r = encrypt.encrypt(map);
        log.debug(r);
        log.debug("{}", r.length());
        log.debug(encrypt.encrypt(map));
    }

    @Test
    public void test_decrypt() {
        String auth = "b3a60e67dfcd220874e36569f623829ea97d556d646b4eb208c2f43" +
                "b452bbf61a3e5982e0a52810517bcc734a5561e2dc53a9e3854d5fd4afebf0b15b7c1ad5c";
        Map<String, Object> token = encrypt.decrypt(auth);
        log.debug("{}", token.get("uid"));
        log.debug("{}", token.get("role"));
    }
}

四、拦截器

HandlerInterceptor (org.springframework.web.servlet.HandlerInterceptor)接口

  • Boolean preHandle()方法:controller方法执行前回调,返回false,则不会继续执行。登录验证等
  • Void postHandle()方法:perHandle()返回true后,controller方法执行后
  • afterCompletion方法:postHandle()执行后,回调
  • Object handle,封装被拦截方法对象

拦截器类

InterceptorRegistry

  • addInterceptor(),添加拦截器组件
  • addPathPatterns(),添加拦截路径
  • excludePathPatterns(),添加排除路径
  • 可声明多个拦截器,按顺序拦截
package com.example.springmvcexamples.example06.interceptor.interceptor;

import com.example.springmvcexamples.example02.handlingexception.exception.MyException;
import com.example.springmvcexamples.example05.textencryptor.EncryptComponent05;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@Component // 将LoginInterceptor06类标记为Spring容器中的组件
@Slf4j // 使用log4j进行日志记录
@RequiredArgsConstructor // 通过构造函数注入依赖,避免在实例化时需要手动注入依赖
public class LoginInterceptor06 implements HandlerInterceptor {
    private final EncryptComponent05 encryptComponent; // 注入加密组件

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token"); // 从请求头中获取token
        if (token == null) {
            throw new MyException(401, "未登录"); // 如果token为空,抛出自定义异常
        }
        Map<String, Object> result = encryptComponent.decrypt(token); // 解密token,获取用户信息
        request.setAttribute("role", result.get("role")); // 将用户角色设置到请求属性中,以便后续处理中使用
        return true; // 返回true表示继续执行后续的拦截器和处理器
    }
}

 配置类

package com.example.springmvcexamples;

import com.example.springmvcexamples.example06.interceptor.interceptor.LoginInterceptor06;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration implements WebMvcConfigurer {

    private final LoginInterceptor06 adminInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(adminInterceptor)
                .addPathPatterns("/api/example06/admin/**");
    }

}

五、定时服务(Timer Service)

定时服务(Timer Service):用以调度安排所有基于定时通知的工作流程

支持指定时间、某一时间后、指定时间间隔内的定时通知

Spring Task Execution and Scheduling

  • 基于TaskExecutor,TaskScheduler接口
  • 基于注解
  • 基于Quartz Scheduler第三方库

@Scheduled

@Scheduled注解是Spring框架中的一个注解,用于标记一个方法为定时任务。它可以与@Scheduled注解一起使用,或者与TaskScheduler接口一起使用。

使用@Scheduled注解的方法会在指定的时间间隔内自动执行

定时任务方法声明在组件内,方法必须无返回值

  • fixedRate,每次执行间隔时间,即使上一任务未执行完依然执行任务(毫秒)
  • fixedDelay,每次执行完成到下一次开始执行间隔时间,即上一任务执行结束后,过指定时间执行任务(毫秒)
  • initialDelay,第一次执行前的延迟时间(毫秒)
  • Cron,指定执行时间表达式

Cron表达式

顺序:秒、分、时、日、月、星期(数字或单词缩写)、年(可省略,即每年)。值为数字或符号

默认不支持从后计算(不支持L) 

启动类

package com.example.springmvcexamples;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

@SpringBootApplication
@EnableScheduling
public class SpringmvcExamplesApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringmvcExamplesApplication.class, args);
    }
}

 @EnableScheduling注解

@EnableScheduling注解是Spring框架中的一个注解,用于开启定时任务功能。在Spring Boot应用中,可以通过在主类上添加该注解来启用定时任务。

组件类

package com.example.springmvcexamples.example07.timer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class MyTimer {
    @Scheduled(cron = "0 0 8 10 * ?")
    public void paySalary() {
        log.debug("Your salary has been paid!");
    }
}

 

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

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

相关文章

2023中国品牌节金谱奖荣誉发布 酷开科技获颁OTT行业科技创新奖

11月17日—19日&#xff0c;以“复苏与腾飞”为主题的2023第十七届中国品牌节&#xff0c;在杭州市云栖小镇国际会展中心成功举行。在18日晚间的荣耀盛典上&#xff0c;“TopBrand 2023中国品牌节金谱奖”荣誉发布&#xff0c;酷开科技斩获OTT行业科技创新奖。 酷开科技作为OTT…

JavaWeb笔记之JavaWeb JDBC

//Author 流云 //Version 1.0 一. 引言 1.1 如何操作数据库 使用客户端工具访问数据库&#xff0c;需要手工建立连接&#xff0c;输入用户名和密码登录&#xff0c;编写 SQL 语句&#xff0c;点击执行&#xff0c;查看操作结果&#xff08;结果集或受影响行数&#xff09;。…

MySQL的增删改查(进阶)--上

1. 数据库约束 1.1 约束类型 NOT NULL - 指示某列不能存储 NULL 值。 UNIQUE - 保证某列的每行必须有唯一的值。 DEFAULT - 规定没有给列赋值时的默认值。 PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列&#xff08;或两个列多个列的结合&#xff09;有唯一标识&#xf…

3-10岁孩子数学发展里程碑

文章目录 3岁4岁5岁6-7岁&#xff08;1-2年级&#xff09;8-9岁&#xff08;3-4年级&#xff09;10岁&#xff08;5年级&#xff09; 当然&#xff0c;孩子的数学能力发展会因个体差异而有所不同&#xff0c;但以下是一个大致的指导&#xff0c;用来描述从3岁到10岁孩子在数学上…

HPM6750系列--第十一篇 Uart讲解(轮询模式)

一、目的 在介绍完GPIO的相关内容下一个必须介绍的就是uart了&#xff0c;因为串口一个主要用途就是用于调试信息打印。 HPM6750在uart的配置上也是相当炸裂&#xff0c;有17个串口&#xff1b;结合HPM6750的高主频高内存&#xff0c;完全可以作为一个串口服务器。 ​​​​​​…

Pycharm enable IntelliBot #patched后,工程无法打开

#本地环境# Pycharm&#xff1a;2023.12 Pro 对应robot pkg版本&#xff1a; robotframework 6.1 robotframework-databaselibrary 1.2.4 robotframework-pythonlibcore 4.1.2 robotframework-requests 0.9.4 robotframework-seleniumlibrary 6.1.…

新增工具箱管理功能、重构网站证书管理功能,1Panel开源面板v1.9.0发布

2023年12月18日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.9.0版本。 在这一版本中&#xff0c;1Panel引入了新的工具箱管理功能&#xff0c;包含Swap分区管理、Fail2Ban管理等功能。此外&#xff0c;1Panel针对网站证书管理功能进行了全面重构&…

移动云捐赠三款开源项目,加速新一代基础软件生态繁荣

随着云计算、大数据、人工智能等新领域新信息技术的发展&#xff0c;我国基础软件的自主可控极大程度地影响着产业链上下游的多样性和技术创新的发展空间。移动云作为中国移动涉云业务的主入口&#xff0c;一直坚持共享开源价值&#xff0c;积极推动中国开源软件生态的繁荣发展…

UE5 水材质注意要点

1、两个法线反向交替流动&#xff0c;可以去观感假的现象 2、水面延边的透明度低 3、增加水面延边的浪花 4、增加折射 折射要整体质量至少在High才有效果 改为半透明材质没有法线信息&#xff1f; 5、处理反射效果 勾选为true 找到这个放在水域 勾为false&#xff0c;即可有非…

基于ssm品牌手机销售信息系统论文

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于品牌手机销售信息系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了品牌手机销售信息系统&#xff0c;它彻底…

05_Web框架之Django二

Web框架之Django二 学习目标和内容 1、能够使用类视图的写法 2、能够使用模板语言的相关语法 3、能够理解过滤器的作用 4、能够理解并使用模板继承 一、类视图 1、类视图介绍 视图是一个可调用的对象&#xff0c;它接收一个请求然后返回一个响应&#xff0c;这个可调用对象可…

phpmyadmin4.8.1远程文件包含漏洞 [GWCTF 2019]我有一个数据库1

打开题目 我们用dirsearch扫描一下后台看看 扫描结果如下 我们访问一下robots.txt看看&#xff0c;提示有phpinfo.php 那我们访问一下phpinfo.php 发现没有任何信息后我们转去看看phpmyadmin看看 成功访问到页面 在这里我们看到phpmyadmin的版本号是4.8.1 我们百度搜索一下看…

Java版企业电子招投标系统源代码,支持二次开发,采用Spring cloud微服务架构

招投标管理系统是一个集门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理于一体的综合性应用平台。它适用于招标代理、政府采购、企业采购和工程交易等业务的企业&#xff0c;旨在提高项目管理的效率和质量。该系…

linux搭建gitlab

gitlab的介绍 区别于github&#xff0c;github是面向互联网基于git实现的代码托管平台&#xff0c;gitlab是基于Ruby语言实现的git管理平台软件&#xff0c;一般用于公司内部代码仓库。 gitlab组成 Nginx 静态Web服务器Gitlab-workhorse 轻量级的反向代理服务器Gitlab-shell 用…

spring MVC概述和土门案例(无配置文件开发)

SpringMVC 1&#xff0c;SpringMVC概述2&#xff0c;SpringMVC入门案例2.1 需求分析2.2 案例制作步骤1:创建Maven项目步骤2:补全目录结构步骤3:导入jar包步骤4:创建配置类步骤5:创建Controller类步骤6:使用配置类替换web.xml步骤7:配置Tomcat环境步骤8:启动运行项目步骤9:浏览器…

Android 大版本升级变更截图方法总结

Android 大版本升级变更截图方法总结 一、Android R (11) 平台二、Android S (12) 平台三、Android U (14) 平台 Android 原生的截屏功能是集成在 SystemUI 中&#xff0c;因此我们普通应用想要获取截图方法&#xff0c;就需要研读下 SystemUI 截屏部分的功能实现。 一、Androi…

RocketMQ系统性学习-SpringCloud Alibaba集成RocketMQ以及消费收发实战

文章目录 Spring Cloud Alibaba 集成 RocketMQ 最佳实践集成依赖DashBoard消息收发实战 Spring Cloud Alibaba 集成 RocketMQ 最佳实践 SpringBoot 相对于 SSM 来说已经很大程度上简化了开发&#xff0c;但是使用 SpringBoot 集成一些第三方的框架&#xff0c;还是需要花费一些…

Nginx快速入门:安装目录结构详解及核心配置解读(二)

0. 引言 上节我们讲解了nginx的应用场景和安装&#xff0c;本节继续针对nginx的各个目录文件进行讲解&#xff0c;让大家更加深入的认识nginx。并通过一个实操案例&#xff0c;带大家来实际认知nginx的核心配置 1. nginx安装目录结构 首先nginx的默认安装目录为&#xff1a;…

【一】FPGA实现SPI协议之SPI协议介绍

【一】FPGA实现SPI协议之SPI协议介绍 一、spi协议解析 spi协议有4根线&#xff0c;主机输出从机输入MOSI、主机输入从机输出MISO、时钟信号SCLK、片选信号SS\CS 。 一般用于主机和从机之间通信。由主机发起读请求和写请求&#xff0c;主机的权限是主动的&#xff0c;从机是被…

web打印技术方案

在B/S应用系统开发中常常遇到表单打印需求&#xff0c;尤其是OA、ERP类的企业运营管理系统&#xff0c;打印的需求很常见&#xff0c;但WEB应用的打印一直以来是一个难题&#xff0c;特别是在应用中完成标签打印&#xff08;如包裹面单、货运标签等&#xff09;、票据打印&…