当前位置: 首页 > news >正文

SpringSecurity认证

文章目录

  • 登陆校验流程
  • 依赖
  • yaml
  • 实现
    • 建表、工具类、实体类
    • 加密器、AuthenticationManager
    • 登录逻辑
    • 登录过滤器、配置过滤器
    • 登出

登陆校验流程

认证
在这里插入图片描述
在这里插入图片描述
登录:
​ ①自定义登录接口
​ 调用ProviderManager的方法进行认证
如果认证通过生成token,根据userId把用户信息存入redis中,返回token给前端
​ ②自定义UserDetailsService
​ 在这个实现类中去查询数据库,封装为UserDetails对象返回

校验:
​ ①定义Jwt认证过滤器
​ 获取token
​ 解析token获取其中的userid
​ 从redis中获取用户信息
​ 存入SecurityContextHolder

依赖

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><!--redis依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--fastjson依赖-->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.33</version>
</dependency>
<!--jwt依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version>
</dependency><!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency><!-- security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

yaml

spring:datasource:url: jdbc:mysql://192.168.111.101:3306/security?characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver

实现

建表、工具类、实体类

建表语句


CREATE TABLE `user` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`user_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',`nick_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',`password` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',`status` CHAR(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',`email` VARCHAR(64) DEFAULT NULL COMMENT '邮箱',`phonenumber` VARCHAR(32) DEFAULT NULL COMMENT '手机号',`sex` CHAR(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',`avatar` VARCHAR(128) DEFAULT NULL COMMENT '头像',`user_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',`create_by` BIGINT(20) DEFAULT NULL COMMENT '创建人的用户id',`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',`update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人',`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',`del_flag` INT(11) DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'

JWT工具类


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;/*** JWT工具类*/
public class JwtUtil {//有效期为public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时//设置秘钥明文public static final String JWT_KEY = "xd52s8w";public static String getUUID(){String token = UUID.randomUUID().toString().replaceAll("-", "");return token;}/*** 生成jtw* @param subject token中要存放的数据(json格式)* @return*/public static String createJWT(String subject) {JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间return builder.compact();}/*** 生成jtw* @param subject token中要存放的数据(json格式)* @param ttlMillis token超时时间* @return*/public static String createJWT(String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间return builder.compact();}private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;SecretKey secretKey = generalKey();long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);return Jwts.builder().setId(uuid)              //唯一的ID.setSubject(subject)   // 主题  可以是JSON数据.setIssuer("sg")     // 签发者.setIssuedAt(now)      // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);}/*** 创建token* @param id* @param subject* @param ttlMillis* @return*/public static String createJWT(String id, String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间return builder.compact();}public static void main(String[] args) throws Exception {String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";Claims claims = parseJWT(token);System.out.println(claims);}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析** @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}}

实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;/*** 用户表(User)实体类*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/private Long id;/*** 用户名*/private String userName;/*** 昵称*/private String nickName;/*** 密码*/private String password;/*** 账号状态(0正常 1停用)*/private String status;/*** 邮箱*/private String email;/*** 手机号*/private String phonenumber;/*** 用户性别(0男,1女,2未知)*/private String sex;/*** 头像*/private String avatar;/*** 用户类型(0管理员,1普通用户)*/private String userType;/*** 创建人的用户id*/private Long createBy;/*** 创建时间*/private Date createTime;/*** 更新人*/private Long updateBy;/*** 更新时间*/private Date updateTime;/*** 删除标志(0代表未删除,1代表已删除)*/private Integer delFlag;
}

UserDetails: loadUserByUsername 方法返回的数据

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {private User user;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return user.getPassword ();}@Overridepublic String getUsername() {return user.getUserName ();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

UserDetailsService ,通过loadUserByUsername获取用户,封装成UserDetails 对象返回

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.security.mapper.UserMapper;
import com.example.security.vo.LoginUser;
import com.example.security.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 查询用户信息LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<> ();queryWrapper.eq (User::getUserName, username);User user = userMapper.selectOne (queryWrapper);if (user == null) {// 用户不存在throw new RuntimeException ("用户不存在");}//TODO 根据用户查询权限信息 添加到LoginUser中return new LoginUser (user);}
}

加密器、AuthenticationManager

密码加密存储、登陆接口

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 密码加密存储** @return*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder ();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf ().disable ()//不通过Session获取SecurityContext.sessionManagement ().sessionCreationPolicy (SessionCreationPolicy.STATELESS).and ().authorizeRequests ()// 对于登录接口 允许匿名访问.antMatchers ("/user/login").anonymous ()// 除上面外的所有请求 全部需要鉴权认证.anyRequest ().authenticated ();}/*** 登陆接口* 通过AuthenticationManager的authenticate方法来进行用户认证* 所以需要在SecurityConfig中配置把AuthenticationManager注入容器** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean ();}
}

登录逻辑

LoginServiceImpl 登录逻辑

import com.alibaba.fastjson.JSONObject;
import com.example.security.config.JwtUtil;
import com.example.security.vo.LoginUser;
import com.example.security.vo.ResponseResult;
import com.example.security.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Objects;@Service
public class LoginServiceImpl implements LoginServcie {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate StringRedisTemplate redisTemplate;@Overridepublic ResponseResult login(User user) {UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken (user.getUserName (), user.getPassword ());Authentication authenticate = authenticationManager.authenticate (authenticationToken);if (Objects.isNull (authenticate)) {throw new RuntimeException ("用户名或密码错误");}// 使用userId生成tokenLoginUser loginUser = (LoginUser) authenticate.getPrincipal ();String userId = loginUser.getUser ().getId ().toString ();String token = JwtUtil.createJWT (userId);// authenticate存入redisredisTemplate.opsForValue ().set ("login:" + userId, JSONObject.toJSONString (loginUser));// 把token响应给前端HashMap<String, String> map = new HashMap<> ();map.put ("token", token);return new ResponseResult (200, "登陆成功", map);}
}

登录过滤器、配置过滤器

对需要登录的接口进行过滤,从redis中查询用户信息,未登录就 不放行

import com.alibaba.fastjson.JSONObject;
import com.example.security.vo.LoginUser;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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.util.Objects;@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate StringRedisTemplate redisTemplate;/*** 对于需要登录的接口进行拦截* 看看用户信息是否存在** @param request* @param response* @param filterChain* @throws ServletException* @throws IOException*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 获取 tokenString token = request.getHeader ("token");if (!StringUtils.hasText (token)) {// 不携带token,放行filterChain.doFilter (request, response);return;}//解析tokenString userId;try {Claims claims = JwtUtil.parseJWT (token);userId = claims.getSubject ();} catch (Exception e) {e.printStackTrace ();throw new RuntimeException ("token非法");}//从redis中获取用户信息String redisKey = "login:" + userId;LoginUser loginUser = JSONObject.parseObject (redisTemplate.opsForValue ().get (redisKey), LoginUser.class);if (Objects.isNull (loginUser)) {// 没有token,就是未登录throw new RuntimeException ("用户未登录");}//存入SecurityContextHolder//TODO 获取权限信息封装到Authentication中UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken (loginUser, null, null);SecurityContextHolder.getContext ().setAuthentication (authenticationToken);// 放行filterChain.doFilter (request, response);}
}

配置过滤器,使其生效

   @Autowiredprivate JwtAuthenticationTokenFilter filter;@Overrideprotected void configure(HttpSecurity http) throws Exception {// 把token校验过滤器 添加到过滤器链中http.addFilterBefore (filter, UsernamePasswordAuthenticationFilter.class);http//关闭csrf.csrf ().disable ()//不通过Session获取SecurityContext.sessionManagement ().sessionCreationPolicy (SessionCreationPolicy.STATELESS).and ().authorizeRequests ()// 对于登录接口 允许匿名访问.antMatchers ("/user/login").anonymous ()// 除上面外的所有请求 全部需要鉴权认证.anyRequest ().authenticated ();}

登出

将用户信息从Redis中删除
登出


public ResponseResult logout() {Authentication authentication = SecurityContextHolder.getContext ().getAuthentication ();LoginUser loginUser = (LoginUser) authentication.getPrincipal ();Long userid = loginUser.getUser ().getId ();redisTemplate.delete ("login:" + userid);return new ResponseResult (200, "退出成功");
}

相关文章:

SpringSecurity认证

文章目录登陆校验流程依赖yaml实现建表、工具类、实体类加密器、AuthenticationManager登录逻辑登录过滤器、配置过滤器登出登陆校验流程 认证 登录&#xff1a; ​ ①自定义登录接口 ​ 调用ProviderManager的方法进行认证 如果认证通过生成token&#xff0c;根据userId把用…...

Socket套接字

概念 Socket套接字&#xff0c;是由系统提供用于网络通信的技术&#xff0c;是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。 分类 Socket套接字主要针对传输层协议划分为如下三类&#xff1a; 流套接字&#xff1a;使用传输层TCP…...

mysql详解之innoDB

索引 Mysql由索引组织&#xff0c;所以索引是mysql多重要概念之一。 聚簇索引 InnoDB和MyISAm一样都是采用B树结构&#xff0c;但不同点在于InnoDB是聚簇索引&#xff08;或聚集索引&#xff09;&#xff0c;将数据行直接放在叶子节点后面。 这里可能存在一个误区&#xff1…...

电信运营商的新尝试:探索非通信领域的发展

近年来&#xff0c;随着电信运营商竞争的日趋激烈和网络建设的成本不断攀升&#xff0c;许多电信运营商已经开始缩减IT投资。然而&#xff0c;在如此情况下&#xff0c;电信运营商仍然需要寻找新的增长机会。那么&#xff0c;在持续缩减IT投资的情况下&#xff0c;电信运营商可…...

第07章_单行函数

第07章_单行函数 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 1. 函数的理解 1.1 什么是函数 函数在计算机语言的使用中贯穿始终&#xff0c;函数的作用是什么呢&#xff1f;它可以把我们经…...

Echarts实现多柱状图重叠重叠效果

有两种重叠效果: 1. 多个柱子重叠为一个 2. 多个柱子重叠为两组 第一种,图例: 这个灰色不是阴影哦, 是柱子. 1. 使用详解 (1) series.Z 折线图组件的所有图形的 z 值。控制图形的前后顺序。 z 值小的图形会被 z 值大的图形覆盖。z 相比 zlevel 优先级更低&#xff0c;而且不会…...

PHP学习笔记(一谦四益)

前言 上一篇文章 PHP学习笔记&#xff08;观隅反三&#xff09;分享了数组的知识&#xff0c;这篇文章接着分享和数组相关的算法。 算法效率 算法效率分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率。时间效率被称为时间复杂度&#xff0c;而空间效率被称…...

Jvm -堆对象的划分

堆对于一个jvm进程来说是唯一的&#xff0c;一个进程只有一个jvm&#xff0c;但是进程半酣多个线程&#xff0c;多个线程共享一个堆。 也就是说&#xff0c;一个jvm实例只存在一个堆&#xff0c;同时对也是Java内存管理的核心区域。 Java堆区域的大小在jvm启动时就已经被确定…...

2023美赛F题讲解+数据领取

我们给大家准备了F题的数据&#xff0c;免费领取&#xff01;在文末 国内生产总值(GDP)可以说是一个国家经济健康状况最著名和最常用的指标之--。它通常用于确定一个国家的购买力和获得贷款的机会,为各国提出提高GDP的政策和项目提供动力。GDP“衡量一个国家在给定时间段内生产…...

【博客625】keepalived开启garp refresh的重要性

keepalived开启garp refresh的重要性 1、场景 1-1、对keepavlied master机器热迁移后出现vip不通&#xff0c;过后恢复 原因&#xff1a;机器迁移后网关那边的arp表没有刷新&#xff0c;流量还是转发到老的端口&#xff0c;但是机器已经迁移到别的端口了&#xff0c;于是网络…...

nginx防护规则,拦截非法字符,防止SQL注入、防XSS,nginx过滤url访问,屏蔽垃圾蜘蛛,WordPress安全代码篇

nginx防护规则,拦截非法字符,防止SQL注入、防XSS,nginx过滤url访问,屏蔽垃圾蜘蛛,WordPress安全代码篇 精心强化,小白一键复制 资源宝分享:www.httple.net 宝塔为例:/www/server/panel/vhost/nginx/你的网站域名.conf,复制代码点击保存 修改www.xx.net你自己域名incl…...

【计算机网络】网络层

文章目录网络层概述网络层提供的两种服务IPv4地址IPv4地址概述分类编址的IPv4地址划分子网的IPv4地址无分类编址的IPv4地址IPv4地址的应用规划IP数据报的发送和转发过程静态路由配置及其可能产生的路由环路问题路由选择路由选择协议概述路由信息协议RIP的基本工作原理开放最短路…...

产品经理知识体系:1.什么是互联网思维?

互联网思维 思考 笔记 用户思维 是要注重用户体验&#xff0c;产品带给用户的价值是什么&#xff0c;是能帮助用户获取想要的商品、解决生活中的问题、获取想要的信息&#xff0c;还是产品能通过兜售参与感、满足感等来满足用户的心理需求。 贯穿产品的整个生命周期过程。 简…...

【数据结构】单链表的接口实现(附图解和源码)

单链表的接口实现&#xff08;附图解和源码&#xff09; 文章目录单链表的接口实现&#xff08;附图解和源码&#xff09;前言一、定义结构体二、接口实现&#xff08;附图解源码&#xff09;1.开辟新空间2.头插数据3.头删数据4.打印整个单链表5.尾删数据6.查找单链表中的数据7…...

TikTok话题量超30亿,这款承载美好记忆的剪贴簿引发讨论

回忆风剪贴簿在TikTok引起关注小超在浏览超店有数后台时发现&#xff0c;有一款平平无奇的剪贴簿的种草视频爆火&#xff0c;在24h内收获了9.9K点赞&#xff0c;播放量更是突破了100W&#xff0c;直接冲到了【种草视频飙升榜】第六名的位置&#xff0c;并且这个数字目前仍在继续…...

了解Dubbo

1.注册中心挂了&#xff0c;消费者还能不能调用生产者&#xff1f; 注册中心挂了&#xff0c; 消费者依然可以调用生产者。生产者和消费者都会在本地缓存注册中心的服务列表&#xff0c;当注册中心宕机时&#xff0c;消费者会读取本地的缓存数据&#xff0c;直接访问生产者&am…...

2023年前端面试知识点总结(JavaScript篇)

近期整理了一下高频的前端面试题&#xff0c;分享给大家一起来学习。如有问题&#xff0c;欢迎指正&#xff01; 1. JavaScript有哪些数据类型 总共有8种数据类型&#xff0c;分别是Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt Null 代表的含义是空对象…...

jQuery

文章目录jQuery 介绍初体验核心函数jQuery 对象和 dom 对象区分什么是 jQuery 对象&#xff0c;什么是 dom 对象问题&#xff1a;jQuery 对象的本质是什么&#xff1f;jQuery 对象和 Dom 对象使用区别Dom 对象和 jQuery 对象互转&#xff08;重点&#xff09;jQuery 选择器&…...

强化学习基础概念

强化学习入门 入门学习第一周&#xff1a;基础概念 经验回放&#xff1a; 将sss,agent当前步的action环与境的交互rrr以及下一步的状态st1s_{t1}st1​组成的四元组[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wxhVd0dn-1676710992983)(null)] 组…...

Redis学习【9】之Redis RDB持久化

文章目录一 AOF(Append Only File) 持久化二 AOF 基础配置2.1 AOF的开启2.2 文件名配置2.3 混合式持久化开启2.4 AOF 文件目录配置三 AOF 文件格式3.1 Redis 协议3.2 查看 AOF 文件3.3 清单文件3.4 Rewrite 机制3.4.1 rewrite简介3.4.2 rewrite 计算策略3.4.3 手动开启 rewrite…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...