SpringSecurity+JWT前后端分离架构登录认证
目录
1. 数据库设计
2. 代码设计
登录认证过滤器
认证成功处理器AuthenticationSuccessHandler
认证失败处理器AuthenticationFailureHandler
AuthenticationEntryPoint配置
AccessDeniedHandler配置
UserDetailsService配置
Token校验过滤器
登录认证过滤器接口配置
Spring Security全局配置
util包
测试结果
在SpringSecurity实现前后端分离登录token认证详解_springsecurity前后端分离登录认证-CSDN博客基础上进行重构,实现前后端分离架构登录认证,基本思想相同,借鉴开源Gitee代码进行改造,具有更好的代码规范。
1. 数据库设计
DROP TABLE IF EXISTS `t_auth`;
CREATE TABLE `t_auth` (`id` BIGINT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',`url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路径',`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_auth
-- ----------------------------
INSERT INTO `t_auth` VALUES (1, '删除用户', '/usr/del', 1, '2021-11-26 17:08:11', '2021-11-26 17:07:52');
INSERT INTO `t_auth` VALUES (2, '新增用户', '/usr/add', 1, '2021-11-26 17:08:13', '2021-11-26 17:08:09');
INSERT INTO `t_auth` VALUES (3, '添加产品', '/product/add', 1, '2021-11-26 17:08:42', '2021-11-26 17:08:29');
INSERT INTO `t_auth` VALUES (4, '下架产品', '/product/del', NULL, NULL, '2021-11-26 17:12:17');
INSERT INTO `t_auth` VALUES (5, '注册', '/user/register', NULL, NULL, '2021-11-26 17:13:32');
INSERT INTO `t_auth` VALUES (6, '注销', '/user/logOff', NULL, NULL, '2021-11-26 17:13:50');-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (`id` BIGINT(11) NOT NULL,`name` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色名称',`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES (1, 'ROLE_admin', 1, '2021-11-26 17:08:52', '2021-11-26 17:08:51');
INSERT INTO `t_role` VALUES (2, 'ROLE_dba', 1, '2021-11-26 17:09:10', '2021-11-26 17:09:05');
INSERT INTO `t_role` VALUES (3, 'ROLE_vip', 1, '2021-11-26 17:09:32', '2021-11-26 17:09:25');
INSERT INTO `t_role` VALUES (4, 'ROLE_user', 1, '2021-11-26 17:09:45', '2021-11-26 17:09:42');-- ----------------------------
-- Table structure for t_role_auth
-- ----------------------------
DROP TABLE IF EXISTS `t_role_auth`;
CREATE TABLE `t_role_auth` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`role_id` BIGINT(20) NULL DEFAULT NULL,`auth_id` BIGINT(20) NULL DEFAULT NULL,`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_role_auth
-- ----------------------------
INSERT INTO `t_role_auth` VALUES (1, 1, 3, 1, '2021-11-26 17:11:31', '2021-11-26 17:11:29');
INSERT INTO `t_role_auth` VALUES (2, 1, 4, 1, '2021-11-26 17:11:31', '2021-11-26 17:11:29');
INSERT INTO `t_role_auth` VALUES (3, 4, 5, 1, '2021-11-26 17:14:45', '2021-11-26 17:14:35');
INSERT INTO `t_role_auth` VALUES (4, 4, 6, 1, '2021-11-26 17:14:47', '2021-11-26 17:14:41');-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (`id` BIGINT(11) NOT NULL,`user_id` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '唯一的userId',`username` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',`password` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',`name` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, '120', 'zhangsan', '123456', '张三', 1, '2021-11-26 17:07:03', '2021-11-26 17:06:53');
INSERT INTO `t_user` VALUES (2, '110', 'lisi', '123456', '李四', 1, '2021-11-26 17:07:36', '2021-11-26 17:07:12');-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`user_id` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户唯一userId',`role_id` BIGINT(20) NULL DEFAULT NULL,`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES (1, '120', 1, 1, '2021-11-26 17:10:10', '2021-11-26 17:10:11');
INSERT INTO `t_user_role` VALUES (2, '110', 2, 1, '2021-11-26 17:11:16', '2021-11-26 17:11:13');
2. 代码设计
登录认证过滤器
Spring Security默认的表单登录认证的过滤器是UsernamePasswordAuthenticationFilter,这个过滤器并不适用于前后端分离的架构,因此我们需要自定义一个过滤器。参照UsernamePasswordAuthenticationFilter这个过滤器改造一下。
/*** 登录认证的filter,参照UsernamePasswordAuthenticationFilter,添加到这之前的过滤器*/public class JwtAuthenticationLoginFilter extends AbstractAuthenticationProcessingFilter {/*** 构造方法,调用父类的,设置登录地址/login,请求方式POST*/public JwtAuthenticationLoginFilter() {super(new AntPathRequestMatcher("/login", "POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {//获取表单提交数据String username = request.getParameter("username");String password = request.getParameter("password");//封装到token中提交UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);return getAuthenticationManager().authenticate(authRequest);}
}
认证成功处理器AuthenticationSuccessHandler
上述的过滤器接口一旦认证成功,则会调用AuthenticationSuccessHandler进行处理,因此我们可以自定义一个认证成功处理器进行自己的业务处理,代码如下:
@Component
public class LoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Autowiredprivate JwtUtil jwtUtil;@AutowiredRedisTemplate redisTemplate;@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Authentication authentication) throws IOException {UserDetails userDetails = (UserDetails) authentication.getPrincipal();SecurityContextHolder.getContext().setAuthentication(authentication);Map<String,String> map=new HashMap<>();map.put("username",userDetails.getUsername());//jwt生成tokenString token = jwtUtil.getToken(map);RedisUser redisUser = RedisUser.builder().username(userDetails.getUsername()).password(userDetails.getPassword()).authorities(userDetails.getAuthorities().stream().map(i->i.getAuthority()).collect(Collectors.toList())).build();//将用户信息保存到redis缓存中redisTemplate.opsForValue().set(userDetails.getUsername(),redisUser,12, TimeUnit.HOURS);ResponseUtils.result(httpServletResponse,new ResultMsg(200,"登录成功!",token));}}
认证失败处理器AuthenticationFailureHandler
同样的,一旦登录失败,比如用户名或者密码错误等等,则会调用AuthenticationFailureHandler进行处理,因此我们需要自定义一个认证失败的处理器,其中根据异常信息返回特定的JSON数据给客户端,代码如下:
@Component
public class LoginAuthenticationFailureHandler implements AuthenticationFailureHandler {/*** 一旦登录失败则会被调用*/@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest,HttpServletResponse response,AuthenticationException exception) throws IOException {//TODO 根据项目需要返回指定异常提示,这里演示了一个用户名密码错误的异常//BadCredentialsException 这个异常一般是用户名或者密码错误if (exception instanceof BadCredentialsException){ResponseUtils.result(response,new ResultMsg(200,"用户名或密码不正确!",null));}ResponseUtils.result(response,new ResultMsg(200,"登录失败",null));}
}
AuthenticationEntryPoint配置
AuthenticationEntryPoint这个接口当用户未通过认证访问受保护的资源时,将会调用其中的commence()方法进行处理。
@Component
@Slf4j
public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {ResponseUtils.result(response,new ResultMsg(403,"认证失败,请重新登录!",null));}
}
AccessDeniedHandler配置
AccessDeniedHandler这处理器当认证成功的用户访问受保护的资源,但是权限不够,则会进入这个处理器进行处理。
@Component
public class RequestAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException accessDeniedException) throws IOException {ResponseUtils.result(response,new ResultMsg(403,"权限不足!",null));}
}
UserDetailsService配置
UserDetailsService这个类是用来加载用户信息,包括用户名、密码、权限、角色集合,我们需要实现这个接口,从数据库加载用户信息,代码如下:
@Service
public class JwtTokenUserDetailsService implements UserDetailsService {/*** 查询用户详情的service*/@Autowiredprivate LoginService loginService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//从数据库中查询SecurityUser securityUser = loginService.loadByUsername(username);System.out.println(securityUser);//用户不存在直接抛出UsernameNotFoundException,security会捕获抛出BadCredentialsExceptionif (Objects.isNull(securityUser))throw new UsernameNotFoundException("用户不存在!");return securityUser;}
}
其中的LoginService是根据用户名从数据库中查询出密码、角色、权限,代码如下:
@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate PasswordEncoder passwordEncoder;@AutowiredTUserService tUserService;@AutowiredTRoleService tRoleService;@Nullable@Overridepublic SecurityUser loadByUsername(String username) {//获取用户信息TUser user = tUserService.getByUsername(username);if (Objects.nonNull(user)){SecurityUser securityUser = new SecurityUser();securityUser.setUsername(username);//todo 此处为了方便,直接在数据库存储的明文,实际生产中应该存储密文,则这里不用再次加密securityUser.setPassword(passwordEncoder.encode(user.getPassword()));//查询该用户的角色List<String> userRoles = tRoleService.selectAllByUsername(username);String[] a={};List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(userRoles.toArray(a));securityUser.setAuthorities(authorityList);return securityUser;}return null;}}
UserDetails这个也是个接口,其中定义了几种方法,都是围绕着用户名、密码、权限+角色集合这三个属性,因此我们可以实现这个类拓展这些字段,SecurityUser代码如下:
@Data
public class SecurityUser implements UserDetails {//用户名private String username;//密码private String password;//权限private Collection<? extends GrantedAuthority> authorities;public SecurityUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {this.username = username;this.password = password;this.authorities = authorities;}public SecurityUser(){}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}// 账户是否未过期@Overridepublic boolean isAccountNonExpired() {return true;}// 账户是否未被锁@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
Token校验过滤器
客户端请求头携带了token,服务端肯定是需要针对每次请求解析、校验token,因此必须定义一个Token过滤器,这个过滤器的主要逻辑如下:
-
从请求头中获取accessToken
-
对accessToken解析、验签、校验过期时间
-
校验成功,将authentication存入ThreadLocal中,这样方便后续直接获取用户详细信息。
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {/*** JWT的工具类*/@Autowiredprivate JwtUtil jwtUtil;/*** UserDetailsService的实现类,从数据库中加载用户详细信息*/@Qualifier("jwtTokenUserDetailsService")@Autowiredprivate UserDetailsService userDetailsService;@AutowiredRedisTemplate redisTemplate;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {String token = request.getHeader("token");/*** token存在则校验token* 1. token是否存在* 2. token存在:* 2.1 校验token中的用户名是否失效*/if (!StringUtils.isEmpty(token)){DecodedJWT decodedJWT = jwtUtil.getTokenInfo(token);String username;try {username = decodedJWT.getClaim("username").asString();}catch (Exception e){throw new RuntimeException("token无效");}//从redis缓存中获得对应用户数据RedisUser redisUser = (RedisUser) redisTemplate.opsForValue().get(username);String[] a={};List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(redisUser.getAuthorities().toArray(a));UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(redisUser, null,authorityList);authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));// 将 authentication 存入 ThreadLocal,方便后续获取用户信息SecurityContextHolder.getContext().setAuthentication(authentication);}//继续执行下一个过滤器chain.doFilter(request,response);}
}
登录认证过滤器接口配置
上述定义了一个认证过滤器JwtAuthenticationLoginFilter,这个是用来登录的过滤器,但是并没有注入加入Spring Security的过滤器链中,需要定义配置,代码如下:
@Configuration
public class JwtAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {/*** userDetailService*/@Qualifier("jwtTokenUserDetailsService")@Autowiredprivate UserDetailsService userDetailsService;/*** 登录成功处理器*/@Autowiredprivate LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;/*** 登录失败处理器*/@Autowiredprivate LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;/*** 加密*/@Autowiredprivate PasswordEncoder passwordEncoder;/*** 将登录接口的过滤器配置到过滤器链中* 1. 配置登录成功、失败处理器* 2. 配置自定义的userDetailService(从数据库中获取用户数据)* 3. 将自定义的过滤器配置到spring security的过滤器链中,配置在UsernamePasswordAuthenticationFilter之前* @param*/@Overridepublic void configure(HttpSecurity http) {JwtAuthenticationLoginFilter filter = new JwtAuthenticationLoginFilter();filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));//认证成功处理器filter.setAuthenticationSuccessHandler(loginAuthenticationSuccessHandler);//认证失败处理器filter.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);//直接使用DaoAuthenticationProviderDaoAuthenticationProvider provider = new DaoAuthenticationProvider();//设置userDetailServiceprovider.setUserDetailsService(userDetailsService);//设置加密算法provider.setPasswordEncoder(passwordEncoder);http.authenticationProvider(provider);//将这个过滤器添加到UsernamePasswordAuthenticationFilter之前执行http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);}
}
Spring Security全局配置
上述仅仅配置了登录过滤器,还需要在全局配置类做一些配置,如下:
-
应用登录过滤器的配置
-
将登录接口、令牌刷新接口放行,不需要拦截
-
配置AuthenticationEntryPoint、AccessDeniedHandler
-
禁用session,前后端分离+JWT方式不需要session
-
将token校验过滤器TokenAuthenticationFilter添加到过滤器链中,放在UsernamePasswordAuthenticationFilter之前。
@Configuration
//@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationSecurityConfig jwtAuthenticationSecurityConfig;@Autowiredprivate EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;@Autowiredprivate RequestAccessDeniedHandler requestAccessDeniedHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//禁用表单登录,前后端分离用不上.disable()//应用登录过滤器的配置,配置分离.apply(jwtAuthenticationSecurityConfig).and()// 设置URL的授权.authorizeRequests().antMatchers("/login").permitAll()// anyRequest() 所有请求 authenticated() 必须被认证.anyRequest().authenticated()//处理异常情况:认证失败和权限不足.and().exceptionHandling()//认证未通过,不允许访问异常处理器.authenticationEntryPoint(entryPointUnauthorizedHandler)//认证通过,但是没权限处理器.accessDeniedHandler(requestAccessDeniedHandler).and()//禁用session,JWT校验不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//将TOKEN校验过滤器配置到过滤器链中,否则不生效,放到UsernamePasswordAuthenticationFilter之前.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)// 关闭csrf.csrf().disable();}// 自定义的Jwt Token校验过滤器@Beanpublic TokenAuthenticationFilter authenticationTokenFilterBean() {return new TokenAuthenticationFilter();}/*** 加密算法** @return*/@Bean@Overrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}
}
util包
JWT工具类
@Component
@ConfigurationProperties(prefix = "jwt")
//@Data
public class JwtUtil {private String signature="cbac";private Integer expiration=12;/**** 生成token header.payload.signature*/public String getToken(Map<String,String> payload){Calendar calendar = Calendar.getInstance();calendar.add(Calendar.HOUR, 24); // 24小时JWTCreator.Builder builder = JWT.create();// 构建payloadpayload.forEach(builder::withClaim);// 指定签发时间、过期时间 和 签名算法,并返回tokenString token = builder.withIssuedAt(new Date()).withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(signature));return token;}/**** 获取token信息*/public DecodedJWT getTokenInfo(String token){DecodedJWT verify=JWT.require(Algorithm.HMAC256(signature)).build().verify(token);return verify;}}
结果封装类
public class ResponseUtils {public static void result(HttpServletResponse response, ResultMsg msg) throws IOException {response.setContentType("application/json;charset=UTF-8");ServletOutputStream out = response.getOutputStream();ObjectMapper objectMapper = new ObjectMapper();out.write(objectMapper.writeValueAsString(msg).getBytes("UTF-8"));out.flush();out.close();}
}
测试结果
项目目录结构
相关文章:

SpringSecurity+JWT前后端分离架构登录认证
目录 1. 数据库设计 2. 代码设计 登录认证过滤器 认证成功处理器AuthenticationSuccessHandler 认证失败处理器AuthenticationFailureHandler AuthenticationEntryPoint配置 AccessDeniedHandler配置 UserDetailsService配置 Token校验过滤器 登录认证过滤器接口配置…...

笔试面试题——二叉树进阶(一)
📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、根据二叉树创建字符串1、题目讲解2、思路讲解3、代码实现 二、二叉树的分层遍历1、题目讲…...
Java反射示例
Java反射示例 创建数据类型ReflectPoint.java package com.reflection;import java.util.Date;public class ReflectPoint {private Date birthday new Date();private int x;public int y;public String str1 "ball";public String str2 "basketball"…...
【WinForm.NET开发】实现使用后台操作的窗体
本文内容 创建使用后台操作的窗体使用设计器创建 BackgroundWorker添加异步事件处理程序添加进度报告和取消支持Checkpoint 如果某项操作需要很长的时间才能完成,并且不希望用户界面 (UI) 停止响应或阻塞,则可以使用 BackgroundWorker 类在另一个线程上…...

【操作系统和计网从入门到深入】(四)基础IO和文件系统
前言 这个专栏其实是博主在复习操作系统和计算机网络时候的笔记,所以如果是博主比较熟悉的知识点,博主可能就直接跳过了,但是所有重要的知识点,在这个专栏里面都会提到!而且我也一定会保证这个专栏知识点的完整性&…...

四.Winform使用Webview2加载本地HTML页面并互相通信
Winform使用Webview2加载本地HTML页面并互相通信 往期目录本节目标核心代码实现HTML代码实现的窗体Demo2代码效果图 往期目录 往期相关文章目录 专栏目录 本节目标 实现刷新按钮点击 C# winform按钮可以调用C# winform代码显示到html上点击HTML按钮可以调用C# winform代码更…...
如何有效清理您的Python环境:清除Pip缓存
Python是一个广泛使用的高级编程语言,以其强大的库和框架而闻名。然而,随着时间的推移和不断安装新的包,Python环境可能会变得混乱不堪,尤其是pip缓存可能占用大量的磁盘空间。本文将向您展示如何有效地清理pip缓存,保…...

Jira 母公司全面停服 Server 产品,用户如何迁移至极狐GitLab
Jira 母公司即将全面停服旗下部分 Server 端产品的销售和服务支持! Jira 母公司 Atlassian 在几年前确定了公司的战略为“全面上云”,为此做出了停止 Server 产品的销售和支持。整个时间线从 2021 年 2 月 2 日开始,直到今年 2 月 15 日&…...

Docker安装配置OnlyOffice
OnlyOffice 是一款强大的办公套件,你可以通过 Docker 轻松安装和部署它。本文将指导你完成安装过程。 步骤 1:拉取 OnlyOffice Docker 镜像 首先,使用以下命令从 Docker Hub 拉取 OnlyOffice Document Server 镜像: sudo docke…...

启动低轨道卫星LEO通讯产业与6G 3GPP NTN标准
通讯技术10年一个大跃进,从1990年的2G至2000年的3G网路,2010年的4G到近期2020年蓬勃发展的5G,当通讯技术迈入融合网路,当前的 5G 技术不仅可提供高频宽、低延迟,同时可针对企业与特殊需求以 5G 专网的模式提供各式服务…...

PICO Developer Center 创建和调试 ADB 命令
PICO 开发者中心概览 ADB 是一个轻量级的 Android 调试桥(Android Debug Bridge,简称 ADB),用于与 Android 设备进行通信和调试。ADB提供了许多有用的功能,使开发人员能够轻松地管理和调试设备上的应用程序。 你可以使用 PDC 工具来调试系统…...
【VRTK】【PICO】如何快速创建一个用VRTK开发的PICO项目
【背景】 每次新建一个VRTK的PICO项目总是做一些重复工作,于是就想着搞成一个基本的包,把基本的设置都放进去,今后新做项目直接导这个包就行了。 完整资源包请见本篇博客的绑定资源。 【内容简介】 这个包是我为了快速开发基于VRTK的PICO应用设置的基础项目包。每次开发…...

国产操作系统:VirtualBox安装openKylin-1.0.1虚拟机并配置网络
国产操作系统:VirtualBox安装openKylin-1.0.1虚拟机并配置网络 openKylin 操作系统目前适配支持X86、ARM、RISC-V三个架构的个人电脑、平板电脑及教育开发板,可以满足绝大多数个人用户及开发者的使用需求。适用于在VirtualBox平台上安装openKylin-1.0.1…...

本地git切换地区后,无法使用ssh访问github 22端口解决方案
问题 由于放假回家,发现之前一直使用正常的git,与github无法通讯,pull和push都无法连接。报错如下: connect to host github.com port 22: Connection timed out fatal: Could not read from remote repository. 原因 可能是所…...
Chat2DB:AI赋能的多数据库客户端工具,开源领航未来数据库管理
Chat2DB:开源多数据库客户端的AI革新 Chat2DB使用教程:Chat2DB使用教程_哔哩哔哩_bilibili 引言: 随着企业数据的快速膨胀,数据库管理的复杂性也在增加。此时,一个能够跨越数据库边界、并且集成先进的AI功能的工具,不…...
SQL Server修改数据字段名的方法
1. ALTER TABLE语句修改 这是一种最常用的数据库更改字段的方法,使用Alter Table语句来更改数据库字段的名称。 一般格式如下: ALTER TABLE 表名 RENAME COLUMN 原字段名 TO 新字段名; 例如,修改字段名字段名从UserName到Uname:…...

Flutter编译报错Connection timed out: connect
背景:用Android Studo 创建了Flutter项目,编译运行报错java.net.ConnectException: Connection timed out: connect 我自己的环境: windows11 Android Studio Flutter 截图如下: 将错误日志展开之后: Exception…...
PG DBA培训26:PostgreSQL运维诊断与监控分析
本课程由风哥发布的基于PostgreSQL数据库的系列课程,本课程属于PostgreSQL Diagnosis and monitoring analysis,学完本课程可以掌握PostgreSQL日常运维检查-风哥PGSQL工具箱,风哥专用PGSQL工具箱介绍,风哥专用PGSQL工具箱使用&…...
运维之道—生产环境安装Redis
目录 1.前言 2.环境准备 2.1 安装gcc依赖 3.部署安装 3.1 下载redis安装包 3.2 解压并编译安装redis 3.3 配置redis 编辑3.4 启动redis并测试 4. 总结 1.前言 大家好,运维之道的系列文章继续进行,我们今天整理的是Redis生产环境的安装,Redis的安装以及生产环境的…...
人工智能数学验证工具LEAN4【入门介绍3】乘法世界-证明乘法的所有运算律
视频链接,创作不易记得投币哦: import Game.Levels.Multiplication.L08add_mul World "Multiplication" Level 9 Title "mul_assoc" namespace MyNat Introduction " We now have enough to prove that multiplication is a…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...