Spring Security——结合JWT实现令牌的验证与授权

news2024/11/25 13:42:25

目录

JWT(JSON Web Token)

项目总结

新建一个SpringBoot项目

pom.xml

PayloadDto

JwtUtil工具类

MyAuthenticationSuccessHandler(验证成功处理器)

JwtAuthenticationFilter(自定义token过滤器)

WebSecurityConfig配置类

WebSecurityController控制器

 index.html

项目测试


  • 本文以Spring Security项目为基础,此处只给出有关JWT的核心代码,其他代码可查看文章:http://t.csdnimg.cn/hLyKB
  • 参考文章:JWT 基础概念详解 | JavaGuide

JWT(JSON Web Token)

  • 基于token的认证方式相较于Session认证方式的好处:
    • 可以节省服务器端的开销,因为每个认证用户占一个Session对象是需要消耗服务器内存资源的,而token是在每次请求时传递给服务器端的
    • 当服务器端做集群部署时,基于token的认证方式也更容易扩展
    • 无须考虑CSRF,由于不同依赖cookie,所以采用token认证方式不会发生CSRF
    • 更适合于移动端,当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会更简单
  • JWT就是token的一种实现方式
  • JWT可以用于认证和信息交换,流程:
    • 在Web应用程序中,当用户成功登录后,服务器可以生成一个JWT并将其发送回客户端
    • 客户端存储该令牌,并在后续每次请求服务器接口时,都在请求报头中携带JWT
    • 服务器校验JWT签名,得到用户信息,如果验证通过,则根据授权规则返回前端请求的数据
  • JWT是一种用于在网络上安全传输信息的开放标准(RFC 7519),通过使用数字签名(或加密)来验证消息的发送者以及确保消息在传输过程中没有被篡改
  • JWT由三部分组成(用点号(.)分隔开)
    • Header(头部):是一个描述JWT元数据的JSON对象
      • 令牌的类型(JWT)
      • 正在使用的签名算法(如HMAC SHA256或者RSA)
    • Payload(载荷):是一个JSON对象,存放有效信息
      • 三种声明类型,关于用户和附加数据的陈述
      • 1、registered
        • iss(issuer):签发人
        • sub(subject):主题
        • aud(audience):受众
        • exp(expiration time):过期时间
        • nbf(not before):生效时间
        • iat(issued at):签发时间
        • jti(jwt id):JWT的唯一身份标识 
      • 2、public
      • 3、private
    • Signature(签名):对头部和载荷采用单向散列算法生成一个哈希码,以确保数据不会被篡改。签名用于验证消息在此过程中是否被更改,并且对于使用私钥签名的令牌还可以验证JWT的发送者是不是它所声称的
    • JWT=Header.Payload.Signature
  • 优点:
    • 简洁:由于其信息是以JSON对象的形式存储的,因此JWT是非常紧凑的
    • 自包含:JWT包含了所有必要的信息,因此不需要查询数据库来验证令牌
    • 可扩展性:由于其灵活的结构,JWT可以包含任意的额外信息
  • 缺点:
    • 信息是以Base64编码的形式存储的,所以不适合存储敏感信息,例如密码
    • 使用时要小心JWT被盗用或篡改
  • 应用场景:
    • 授权:一旦用户登录,每个后续请求就都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录(Single Sign On)是目前被广泛使用JWT的一个功能,因为它开销小,并且能够轻松地跨不同的域
    • 信息交换:可以在各方之间安全传输信息。因为可以对JWT进行签名(例如使用公钥或私钥对),所以可以确定发送者是特定的人。此外,由于签名是使用标头和有效载荷计算的,因此还可以验证内容是否被篡改

项目总结

  • 工作流程:

  • 1、用户注册和登录:

    • 用户通过注册接口提交注册信息,系统保存用户信息到数据库。
    • 用户通过登录接口提交用户名和密码,系统验证用户信息,成功后生成并返回JWT令牌。
  • 2、请求保护资源:

    • 客户端在每次请求时将JWT令牌放在请求头中。
    • JwtAuthenticationFilter 拦截请求,从请求头中提取JWT令牌,并通过 getAuthentication(token) 方法验证和解析令牌。
    • 若验证成功,将 UsernamePasswordAuthenticationToken 设置到 SecurityContextHolder 中,使当前线程拥有安全上下文。
    • 后续处理链和控制器可以通过安全上下文获取用户信息进行权限验证。
  • 3、安全考量:

    • JWT加密:使用HMAC算法对JWT进行签名,确保令牌的完整性和真实性。
    • 过期时间:设置JWT的有效期,减小令牌被盗用的风险。
    • 角色和权限:通过角色和权限控制资源访问,确保不同用户只能访问其权限范围内的资源。

新建一个SpringBoot项目

 项目的结构

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.study</groupId>
	<artifactId>jwt</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>jwt</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity5</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--hutool是一个Java工具类库-->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.5.7</version>
		</dependency>
		<!--jwt相关依赖-->
		<dependency>
			<groupId>com.nimbusds</groupId>
			<artifactId>nimbus-jose-jwt</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

PayloadDto

package com.study.jwt.dto;

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.List;

/**
 * 用于封装JWT中的有效载荷
 */
@Data
//这个注解单独应用时会生成 equals 和 hashCode 方法,callSuper = false 表示生成的方法不会调用父类的 equals 和 hashCode 方法
@EqualsAndHashCode(callSuper = false)
//用于生成 Builder 模式相关的代码。它会生成一个静态内部类 ExampleBuilder,使得创建对象更加简洁和灵活
@Builder
public class PayloadDto {
    private String sub;//主题
    private Long iat;//签发时间
    private Long exp;//过期时间
    private String jti;//JWT的ID
    private String username;//用户名称
    private List<String> authorities;//用户拥有的权限
}

JwtUtil工具类

package com.study.jwt.util;

import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.study.jwt.dto.PayloadDto;

import java.text.ParseException;
import java.util.Date;

/**
 * 工具类,用于提供生成和验证JWT的方法
 */
public class JwtUtil {
    //默认密钥
    public static final String DEFAULT_SECRET="mySecret";

    /**
     * 使用HMAC SHA-256签名算法
     * @param payloadStr 有效载荷
     * @param secret 密钥
     * @return JWS串
     */
    public static String generateTokenByHMAC(String payloadStr,String secret) throws JOSEException {
        //创建JWS头,设置签名算法和类型
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256).type(JOSEObjectType.JWT).build();
        //将载荷信息封装到Payload中
        Payload payload = new Payload(payloadStr);
        //创建JWS对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        //创建HMAC签名器
        JWSSigner jwsSigner = new MACSigner(secret);
        //签名
        jwsObject.sign(jwsSigner);
        return jwsObject.serialize();
    }

    /**
     * 验证签名,提取有效载荷,以PayloadDto对象形式返回
     * @param token JWS串
     * @param secret 密钥
     * @return PayloadDto对象
     */
    public static PayloadDto verifyTokenByHMAC(String token,String secret) throws ParseException, JOSEException {
        //从token中解析JWS对象
        JWSObject jwsObject = JWSObject.parse(token);
        //创建HMAC验证器
        JWSVerifier jwsVerifier = new MACVerifier(secret);
        if (!jwsObject.verify(jwsVerifier)) {
            throw new JOSEException("token签名不合法!");
        }
        String payload = jwsObject.getPayload().toString();
        PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class);
        if (payloadDto.getExp() < new Date().getTime()){
            throw new JOSEException("token已过期!");
        }
        return payloadDto;
    }
}

MyAuthenticationSuccessHandler(验证成功处理器)

package com.study.jwt.config.handler;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JOSEException;
import com.study.jwt.dto.PayloadDto;
import com.study.jwt.util.JwtUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

/**
 * 修改验证成功处理器,用户成功登录后,在响应报头中发送token
 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        // 获取认证主体信息
        Object principal = authentication.getPrincipal();

        // 用户登录成功后,在响应头中发送 token
        if(principal instanceof UserDetails){
            // 如果主体信息是用户详情实例
            UserDetails user=(UserDetails)principal;

            // 获取用户的权限信息
            Collection<? extends GrantedAuthority> authorities=authentication.getAuthorities();
            List<String> authoritiesList = new ArrayList<>(authorities.size());

            // 将用户权限列表转换为字符串列表
            authorities.forEach(authority -> {
                authoritiesList.add(authority.getAuthority());
            });

            // 定义 token 的创建时间和过期时间
            Date now = new Date();
            DateTime exp = DateUtil.offsetSecond(now, 60 * 60); // 1小时后过期

            // 创建 payload 数据传输对象
            PayloadDto payloadDto=PayloadDto.builder()
                    .sub(user.getUsername())
                    .iat(now.getTime())
                    .exp(exp.getTime())
                    .jti(UUID.randomUUID().toString())
                    .username(user.getUsername())
                    .authorities(authoritiesList)
                    .build();
            String token=null;
            try {
                // 生成 JWT token
                token= JwtUtil.generateTokenByHMAC(
                        //nimbus-jose-jwt所使用的HMAC SHA256算法所需密钥长度为256位(32字节),因此先用md5加密
                        JSONUtil.toJsonStr(payloadDto),
                        SecureUtil.md5(JwtUtil.DEFAULT_SECRET)
                );

                // 设置响应头和响应内容类型
                response.setHeader("Authorization",token);
                response.setContentType("application/json;charset=UTF-8");
                PrintWriter out = response.getWriter();
                out.write("登录成功!");
                out.close();
            } catch (JOSEException e) {
                e.printStackTrace();
            }
        }
    }
}

JwtAuthenticationFilter(自定义token过滤器)

package com.study.jwt.filter;

import cn.hutool.crypto.SecureUtil;
import com.nimbusds.jose.JOSEException;
import com.study.jwt.dto.PayloadDto;
import com.study.jwt.util.JwtUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * JwtAuthenticationFilter是一个过滤器,拦截用户请求,验证token
 * 继承OncePerRequestFilter,保证在任何servlet容器上每次请求调度都能执行一次。
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    /**
     * 过滤器逻辑,提取和验证JWT,如果有效则设置认证信息。
     *
     * @param request     HttpServletRequest对象
     * @param response    HttpServletResponse对象
     * @param filterChain FilterChain对象
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("Authorization"); // 从请求头中获取JWT
        if (token == null) {
            // 如果请求头中没有token,则直接放行
            filterChain.doFilter(request, response);
            return;
        }
        // 如果请求头中有token,则进行解析,并且设置认证信息
        try {
            // 验证并解析token,并将认证信息设置到SecurityContext中
            //它将经过认证的用户信息设置到当前的安全上下文中 (SecurityContext),从而使得后续的安全决策可以基于该用户的信息进行
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
            filterChain.doFilter(request, response); // 继续执行过滤链
        } catch (ParseException | JOSEException e) {
            e.printStackTrace(); // 打印异常信息
        }
    }

    /**
     * 验证token,并解析token,返回以用户名和密码所表示的经过身份验证的主体的令牌。
     *
     * @param token JWT字符串
     * @return UsernamePasswordAuthenticationToken对象
     * @throws ParseException JSON解析异常
     * @throws JOSEException  JWT处理异常
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String token) throws ParseException, JOSEException {
        // 使用HMAC算法验证并解析JWT,获取PayloadDto对象
        PayloadDto payloadDto = JwtUtil.verifyTokenByHMAC(token, SecureUtil.md5(JwtUtil.DEFAULT_SECRET));
        String username = payloadDto.getUsername(); // 从Payload中获取用户名
        List<String> roles = payloadDto.getAuthorities(); // 从Payload中获取角色列表
        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();

        // 将角色列表转换为Spring Security的SimpleGrantedAuthority集合
        roles.forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));

        if (username != null) {
            return new UsernamePasswordAuthenticationToken(username, null, authorities);
        }
        return null; // 如果用户名为空,返回null
    }
}

WebSecurityConfig配置类

package com.study.jwt.config;

import com.study.jwt.filter.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity // 启用Spring Security的Web安全支持
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler; // 成功处理器注入
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler; // 失败处理器注入
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    /**
     * 配置HTTP安全
     *
     * @param http HttpSecurity对象
     * @throws Exception 异常处理
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/", "/home", "/login").permitAll() // 允许所有人访问
                .antMatchers("/admin/**").hasRole("ADMIN") // 只有具有 "ADMIN" 角色的用户才能访问以 "/admin/" 开头的路径
                .anyRequest().hasRole("USER") // 其他请求必须有USER角色
                .and()
            .formLogin()
                .loginPage("/login") // 指定登录页面
                .successHandler(authenticationSuccessHandler) // 登录成功处理器
                .failureHandler(authenticationFailureHandler) // 登录失败处理器
                .and()
            .logout() // 启用注销功能
                .and()
            // 添加自定义的 JWT 过滤器
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable(); // 禁用CSRF保护
    }

    /**
     * 配置密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 使用BCrypt加密密码
    }
}

WebSecurityController控制器

package com.study.jwt.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * 1.将configure(HttpSecurity http)方法中设置的不同的URL映射到不同的页面
 * 2.方法返回的是视图名称,需要视图解析器将视图名称解析成实际的HTML文件
 * 然后访问url就可以跳转到HTML页面了,否则返回的只是一个字符串
 * 3.在application.properties配置文件中配置视图解析器,springboot已经默认配置好了,你不用写了
 */
@Controller
public class WebSecurityController {

    /**
     * 登录后跳转到home.html页面
     */
    @GetMapping("/home")
    public String home(){
        return "home";
    }

    /**
     * 登录页面
     */
    @GetMapping("/login")
    public String login(){
        return "login";//login.html
    }

    /**
     * 当访问/resource时,会重定向到/login,登录后才可以访问受保护的页面resource.html
     */
    @GetMapping("/resource")
    public String resource(){
        return "resource";//resource.html
    }

    @GetMapping("/admin/index")
    public String index(){
        return "index";//index.html
    }
}

 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
这是网站的首页!
</body>
</html>

项目测试

1、访问网址:http://localhost:8080/login

下面的Headers里的Authorization里的Value即为token,它被点号(.)分成三个部分,第一部分为Header,第二部分为Payload,第三部分为Signature

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiIsIlJPTEVfQURNSU4iXSwiZXhwIjoxNzE4NDM4NDY1NDM3LCJpYXQiOjE3MTg0MzQ4NjU0MzcsImp0aSI6ImQyNDFmMDlkLTgwNzYtNDZhZC1hYTg0LTc3MjQxNTIyZjUyZSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.izNsanv4m-UJiIH23m8A2FvqWIvMfy0AEbleanxnMt8

2、访问网址:http://localhost:8080/resource

携带token进行后续访问:把登录时的token复制到Headers里的新增的Authorization里,访问受保护资源 

3、访问网址:http://localhost:8080/admin/index

该地址需要角色为“ADMIN”,所以需要使用第二个账号"admin"登录

如果不用特定账号登录,则返回403错误

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

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

相关文章

idea Alt+/ 自动补全变量名开头是大写 改 选择小写开头变量名

idea 中自动补全变量名是非常常见的操作&#xff0c;变量名一般都需要小写开头&#xff0c;但是idea中 Alt / 自动补全变量名时 补全的变量名是大写的&#xff0c;这就很难受了。如下图所示&#xff1a; AutowiredLogService LogService;Ctrl 空格 快捷键 虽然不像 Alt / 一…

python进阶:多线程原理python(四)

1.并发的概念python基础 举个例子&#xff1a; 1.1并发与并行 并发&#xff1a;逻辑上具备同时处理多个任务的能力 并行&#xff1a; 物理上在同一时刻执行多个并发任务 1.2什么是线程&#xff0c;什么是进程 开个QQ&#xff0c;开了一个进程;开了迅雷&#xff0c;开了一…

【Seata】分布式事务解决方案——理论

目录 回顾什么是事务数据库事务的四大特性&#xff1a;ACID 分布式事务解释面临挑战分布式事务产生场景1. 单体架构中多数据源场景2. 分布式架构场景 分布式事务解决方案jta AtomikosLCN模式问题 Alibaba的Seata解决分布式事务问题 使用MQ解决分布式事务问题问题1&#xff1a;…

2025年计算机毕业设计题目参考

今年最新计算机毕业设计题目参考 以下可以参考 springboot洗衣店订单管理系统 springboot美发门店管理系统 springboot课程答疑系统 springboot师生共评的作业管理系统 springboot平台的医疗病历交互系统 springboot购物推荐网站的设计与实现 springboot知识管理系统 springbo…

管道塞满钢珠,推动一端的钢珠另一端钢珠瞬间掉落,超光速了吗?

管道塞满钢珠&#xff0c;推动一端的钢珠另一端钢珠瞬间掉落&#xff0c;超光速了吗&#xff1f; 现实中我们感觉钢珠瞬间掉落&#xff0c;好像是超光速了&#xff0c;其实不然&#xff0c;那只是我们的错觉。我们日常生活中感受到的速度与光速相比实在太慢了&#xff0c;以至…

MoonBit 亮相港科大「 INNOTECH 创科嘉年华」,技术创新实力备受瞩目

INNOTECH创科嘉年华 6月16日&#xff0c; MoonBit 作为 IDEA 研究院重点项目成果受邀参与一年一度由香港科技大学&#xff08;广州&#xff09;主办的「INNOTECH 创科嘉年华」&#xff0c;作为港科大&#xff08;广州&#xff09;每年最重要的科创实力展示机会&#xff0c;本次…

qmt量化交易策略小白学习笔记第37期【qmt编程之指数数据--如何获取迅投商品市场指数行情数据】

qmt编程之获取商品市场指数数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;咨询免费开通量化回测与获取实盘权限&#xff0c;欢迎和博主联系&#xff01; 获取迅投商…

“AUTOSAR Nvm_WriteAll()掉电无法正常更新Nvm数据” 问题分析

1、问题现象 再掉电时调用Nvm_WriteAll()函数后&#xff0c;再重新上电&#xff0c;发现下电前更新得NvmBlock数据未被正确更新到Nvm中。掉电时直接调用Nvm_WriteBlock()函数&#xff0c;可以正常更新指定得NvmBlock块。 2、排查 NvM_InterWriteAll()函数有被正常执行。NvM_I…

Python期末复习题库(上)

1. (单选题) Python源程序的扩展名为&#xff08; A &#xff09; A. py B. c C. class D. ph 2. (单选题) 下列&#xff08; A &#xff09;符合可用于注释Python代码。 A. # B. */ C. // D. $ 3. (单选题)下列关于Python 语言的特点的说法中&#xff0c;错误的是&#xf…

【C++修行之道】C/C++内存管理

目录 一、C/C内存分布 1. 选择题&#xff1a; 2. 填空题&#xff1a; 3. sizeof 和 strlen 区别&#xff1f; 二、 C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free 1.malloc/calloc/realloc的区别是什么&#xff1f; 2.这里需要free(p2)吗&#xff…

计算机图形学入门15:几何表示

1.几何概论 通过图形学建模表示现实生活中的各种物体&#xff0c;要解决的第一个问题就是如何定义物体形状&#xff0c;而这就涉及到了几何(Geometry)。如下图所示&#xff0c;各种各样的玻璃水杯、车子上的不同部件、水花四溅的模拟、病毒的微观结构等这些几何形状。 那么如何…

Pikachu靶场--文件包含

参考借鉴 Pikachu靶场之文件包含漏洞详解_pikachu文件包含-CSDN博客 文件包含&#xff08;CTF教程&#xff0c;Web安全渗透入门&#xff09;__bilibili File Inclusion(local) 查找废弃隐藏文件 随机选一个然后提交查询 URL中出现filenamefile2.php filename是file2.php&…

MCK主机加固在防漏扫中的关键作用

在当今这个信息化飞速发展的时代&#xff0c;网络安全成为了企业不可忽视的重要议题。漏洞扫描&#xff0c;简称漏扫&#xff0c;是一种旨在发现计算机系统、网络或应用程序中潜在安全漏洞的技术手段。通过自动化工具&#xff0c;漏扫能够识别出系统中存在的已知漏洞&#xff0…

C++项目实战:SPDK文件系统

目录 一、Blobstore设计框架二、Cache机制三、Blob FS I/O操作四、SPDK FUSE (Filesystem in Userspcae) 前言 Blob FS是spdk面向于用户态的轻量级的文件系统 SPDK通过绕过内核(kernel bypass)的方案&#xff0c;构筑了用户态驱动&#xff0c;并利用异步轮询、无锁机制等&a…

函数依赖-函数依赖、平凡函数依赖、完全与部分函数依赖、传递函数依赖

一、引言 函数依赖是关系模式中属性与属性之间存在的一种重要数据依赖 1、将关系模式R的模式结构改为 R&#xff08;SNO&#xff0c;CNO&#xff0c;SN&#xff0c;SD&#xff0c;DD&#xff0c;GRADE&#xff09; 并对属性列进行重命名 R&#xff08;学生学号&#xff0c…

msvcp100.dll已加载但找不到入口点的处理方法,分析比较靠谱的msvcp100.dll解决方法

用户在日常使用中有时会遇到一个错误提示&#xff1a;“已加载 msvcp100.dll&#xff0c;但找不到入口点”。这一信息不仅引发了使用上的不便&#xff0c;也对软件的稳定性产生了质疑。理解并解决该问题不仅对确保计算机正常运行至关重要&#xff0c;也对维护软件的长期稳定性和…

油猴 脚本如何添加包含哪个网址 执行脚本

油猴 脚本如何添加包含哪个网址 执行脚本 在这里面加上就可以 // include *://blog.csdn.net/*/article/details/* // include *.blog.csdn.net/article/details/*

【随手记】贴一段matplotlib绘图的多图拼接代码

二维&#xff1a; import numpy as np import matplotlib.pyplot as pltpic_names ["pic1", "pic2", "pic3", "pic4", "pic5", "pic6", "pic7", "pic8", "pic9", "pic10&qu…

代理四川公司疑难商标办理商标异议复审办理

申请商标注册或者办理其他商标事宜&#xff0c;可以自行办理&#xff0c;也可以委托依法设立的商标代理机构办理。外国人或者外国企业在中国申请商标注册和办理其他商标事宜的&#xff0c;应当委托依法设立的商标代理机构办理&#xff0c;按照被代理人的委托办理商标注册申请或…

BEV 中 multi-frame fusion 多侦融合(一)

文章目录 参数设置align_dynamic_thing:为了将动态物体的点云数据从上一帧对齐到当前帧流程旋转函数平移公式filter_points_in_ego:筛选出属于特定实例的点get_intermediate_frame_info: 函数用于获取中间帧的信息,包括点云数据、传感器校准信息、自车姿态、边界框及其对应…