【SpringSecurity】SpringSecurity+JWT实现登录
1. SpringSecurity介绍
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是为Java应用程序设计的,特别是那些基于Spring的应用程序。Spring Security是一个社区驱动的开源项目,它提供了全面的安全性解决方案,包括防止常见的安全漏洞如CSRF、点击劫持、会话固定等。
以下是Spring Security的一些关键特性和概念:
-
认证(Authentication):Spring Security可以处理用户的身份验证过程,即确认用户是否是他们声称的人。它可以使用多种机制来进行身份验证,例如表单登录、HTTP基本认证、OAuth2、JWT等。
-
授权(Authorization):一旦用户通过了身份验证,Spring Security就会根据用户的权限来决定他们可以访问哪些资源。这可以通过定义角色、权限或更细粒度的访问规则来实现。
-
安全配置:Spring Security可以通过Java配置或XML配置来设置安全策略。通常推荐使用Java配置,因为它与现代Spring应用更为集成,并提供编译时检查。
-
拦截URL模式:可以定义哪些URL需要特定的权限才能访问,以及如何处理未认证或未经授权的请求。
-
过滤器链:Spring Security利用了一组过滤器(
Filter
),这些过滤器在每次HTTP请求时被调用,以执行各种安全相关的任务。开发者可以根据需要添加自定义过滤器。 -
密码编码:为了安全存储用户密码,Spring Security支持多种加密方式,如BCrypt、PBKDF2等。
-
记住我(Remember-Me):允许系统在用户关闭浏览器后仍然保持登录状态,直到明确登出或cookie过期。
-
注销(Logout):提供了安全的退出机制,确保用户的会话被正确地销毁。
-
CSRF保护:默认启用跨站请求伪造攻击防护,确保只有来自合法来源的请求才能修改服务器端的状态。
-
Session管理:可以配置会话创建策略,例如只在需要时创建会话,或者限制同一时间内的并发会话数量。
-
OAuth2和OpenID Connect支持:内置对OAuth2客户端和资源服务器的支持,方便集成第三方认证服务。
使用Spring Security,开发者可以专注于业务逻辑的开发,而将安全问题交给这个成熟可靠的框架来处理。同时,由于其高度可扩展性和灵活性,Spring Security也适合用于构建复杂的安全需求。
2. 登录流程
登录API无需拦截,SpringSecurity直接放行。
/*** @description 认证授权**/
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Api(tags = "认证")
public class AuthController {private final AuthService authService;@PostMapping("/login")@ApiOperation("登录")public ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {String token = authService.createToken(loginRequest);HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.set(SecurityConstants.TOKEN_HEADER, token);return new ResponseEntity<>(httpHeaders, HttpStatus.OK);}
}
AuthService首先会校验用户名与密码,和用户的角色,然后调用JwtTokenUtils创建token,然后以userId为key,token作为value存在Redis中。
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AuthService {private final UserService userService;private final StringRedisTemplate stringRedisTemplate;private final CurrentUserUtils currentUserUtils;public String createToken(LoginRequest loginRequest) {User user = userService.find(loginRequest.getUsername());if (!userService.check(loginRequest.getPassword(), user.getPassword())) {throw new BadCredentialsException("The user name or password is not correct.");}JwtUser jwtUser = new JwtUser(user);if (!jwtUser.isEnabled()) {throw new BadCredentialsException("User is forbidden to login");}List<String> authorities = jwtUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());String token = JwtTokenUtils.createToken(user.getUserName(), user.getId().toString(), authorities, loginRequest.getRememberMe());stringRedisTemplate.opsForValue().set(user.getId().toString(), token);return token;}public void removeToken() {stringRedisTemplate.delete(currentUserUtils.getCurrentUser().getId().toString());}
}
JwtTokenUtils负责创建token、解析token与获取userId。
public class JwtTokenUtils {/*** 生成足够的安全随机密钥,以适合符合规范的签名*/private static final byte[] API_KEY_SECRET_BYTES = DatatypeConverter.parseBase64Binary(SecurityConstants.JWT_SECRET_KEY);private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(API_KEY_SECRET_BYTES);public static String createToken(String username, String id, List<String> roles, boolean isRememberMe) {long expiration = isRememberMe ? SecurityConstants.EXPIRATION_REMEMBER : SecurityConstants.EXPIRATION;final Date createdDate = new Date();final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000);String tokenPrefix = Jwts.builder().setHeaderParam("type", SecurityConstants.TOKEN_TYPE).signWith(SECRET_KEY, SignatureAlgorithm.HS256).claim(SecurityConstants.ROLE_CLAIMS, String.join(",", roles)).setId(id).setIssuer("SnailClimb").setIssuedAt(createdDate).setSubject(username).setExpiration(expirationDate).compact();return SecurityConstants.TOKEN_PREFIX + tokenPrefix; // 添加 token 前缀 "Bearer ";}// userIdpublic static String getId(String token) {Claims claims = getClaims(token);return claims.getId();}// 得到 userName、token与 authoritiespublic static UsernamePasswordAuthenticationToken getAuthentication(String token) {Claims claims = getClaims(token);List<SimpleGrantedAuthority> authorities = getAuthorities(claims);String userName = claims.getSubject();return new UsernamePasswordAuthenticationToken(userName, token, authorities);}private static List<SimpleGrantedAuthority> getAuthorities(Claims claims) {String role = (String) claims.get(SecurityConstants.ROLE_CLAIMS);return Arrays.stream(role.split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());}private static Claims getClaims(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();}
}
3. JWT认证流程
// 启用 SpringSecurity
@EnableWebSecurity
// 启用 SpringSecurity 注解开发
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {private final StringRedisTemplate stringRedisTemplate;public SecurityConfiguration(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}/*** 密码编码器*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors(withDefaults())// 禁用 CSRF.csrf().disable().authorizeRequests()// 指定的接口直接放行// swagger.antMatchers(SecurityConstants.SWAGGER_WHITELIST).permitAll().antMatchers(SecurityConstants.H2_CONSOLE).permitAll().antMatchers(HttpMethod.POST, SecurityConstants.SYSTEM_WHITELIST).permitAll()// 其他的接口都需要认证后才能请求.anyRequest().authenticated().and()//添加自定义Filter.addFilter(new JwtAuthorizationFilter(authenticationManager(), stringRedisTemplate))// 不需要session(不创建会话).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 授权异常处理.exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint()).accessDeniedHandler(new JwtAccessDeniedHandler());// 防止H2 web 页面的Frame 被拦截http.headers().frameOptions().disable();}/*** Cors配置优化**/@BeanCorsConfigurationSource corsConfigurationSource() {org.springframework.web.cors.CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(singletonList("*"));// configuration.setAllowedOriginPatterns(singletonList("*"));configuration.setAllowedHeaders(singletonList("*"));configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "OPTIONS"));configuration.setExposedHeaders(singletonList(SecurityConstants.TOKEN_HEADER));configuration.setAllowCredentials(false);configuration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}
自定义Filter
@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {private final StringRedisTemplate stringRedisTemplate;// 不是 Bean, 需要手动注入public JwtAuthorizationFilter(AuthenticationManager authenticationManager, StringRedisTemplate stringRedisTemplate) {super(authenticationManager);this.stringRedisTemplate = stringRedisTemplate;}@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws IOException, ServletException {String token = request.getHeader(SecurityConstants.TOKEN_HEADER);if (token == null || !token.startsWith(SecurityConstants.TOKEN_PREFIX)) {SecurityContextHolder.clearContext();chain.doFilter(request, response);return;}String tokenValue = token.replace(SecurityConstants.TOKEN_PREFIX, "");UsernamePasswordAuthenticationToken authentication = null;try {// token是否有效String previousToken = stringRedisTemplate.opsForValue().get(JwtTokenUtils.getId(tokenValue));if (!token.equals(previousToken)) {SecurityContextHolder.clearContext();chain.doFilter(request, response);return;}authentication = JwtTokenUtils.getAuthentication(tokenValue);} catch (JwtException e) {logger.error("Invalid jwt : " + e.getMessage());}// 将userName, token, authorities保存在Context中SecurityContextHolder.getContext().setAuthentication(authentication);chain.doFilter(request, response);}
}
SecurityContextHolder是基于ThreadLocal实现的,可以实现不同线程之间的隔离。
public class SecurityContextHolder {public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";public static final String MODE_GLOBAL = "MODE_GLOBAL";public static final String SYSTEM_PROPERTY = "spring.security.strategy";private static String strategyName = System.getProperty("spring.security.strategy");private static SecurityContextHolderStrategy strategy;private static int initializeCount = 0;public SecurityContextHolder() {}private static void initialize() {if (!StringUtils.hasText(strategyName)) {strategyName = "MODE_THREADLOCAL";}if (strategyName.equals("MODE_THREADLOCAL")) {strategy = new ThreadLocalSecurityContextHolderStrategy();} else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {strategy = new InheritableThreadLocalSecurityContextHolderStrategy();} else if (strategyName.equals("MODE_GLOBAL")) {strategy = new GlobalSecurityContextHolderStrategy();} else {try {Class<?> clazz = Class.forName(strategyName);Constructor<?> customStrategy = clazz.getConstructor();strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();} catch (Exception var2) {Exception ex = var2;ReflectionUtils.handleReflectionException(ex);}}++initializeCount;}
}
4. 全局异常处理器
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {/*** 当用户尝试访问需要权限才能的REST资源而不提供Token或者Token错误或者过期时,* 将调用此方法发送401响应以及错误信息*/@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());}
}
public class JwtAccessDeniedHandler implements AccessDeniedHandler {/*** 当用户尝试访问需要权限才能的REST资源而权限不足的时候,* 将调用此方法发送403响应以及错误信息*/@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {accessDeniedException = new AccessDeniedException("Sorry you don not enough permissions to access it!");response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());}
}
5. 注销流程
删除Redis中保存的token。
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AuthService {private final UserService userService;private final StringRedisTemplate stringRedisTemplate;private final CurrentUserUtils currentUserUtils;public String createToken(LoginRequest loginRequest) {User user = userService.find(loginRequest.getUsername());if (!userService.check(loginRequest.getPassword(), user.getPassword())) {throw new BadCredentialsException("The user name or password is not correct.");}JwtUser jwtUser = new JwtUser(user);if (!jwtUser.isEnabled()) {throw new BadCredentialsException("User is forbidden to login");}List<String> authorities = jwtUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());String token = JwtTokenUtils.createToken(user.getUserName(), user.getId().toString(), authorities, loginRequest.getRememberMe());stringRedisTemplate.opsForValue().set(user.getId().toString(), token);return token;}public void removeToken() {stringRedisTemplate.delete(currentUserUtils.getCurrentUser().getId().toString());}
}
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CurrentUserUtils {private final UserService userService;public User getCurrentUser() {return userService.find(getCurrentUserName());}private String getCurrentUserName() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.getPrincipal() != null) {return (String) authentication.getPrincipal();}return null;}
}
6. 权限管理
基于@PreAuthorize实现权限管理
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@RequestMapping("/users")
@Api(tags = "用户")
public class UserController {private final UserService userService;@GetMapping// 有任意角色的权限都可以访问@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_MANAGER','ROLE_ADMIN')")@ApiOperation("获取所有用户的信息(分页)")public ResponseEntity<Page<UserRepresentation>> getAllUser(@RequestParam(value = "pageNum", defaultValue = "0") int pageNum, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();System.out.println("auth信息: " + authentication.getPrincipal().toString() + " 鉴权" + authentication.getAuthorities().toString());System.out.println("***********");Page<UserRepresentation> allUser = userService.getAll(pageNum, pageSize);return ResponseEntity.ok().body(allUser);}@PutMapping@PreAuthorize("hasAnyRole('ROLE_ADMIN')")@ApiOperation("更新用户")public ResponseEntity<Void> update(@RequestBody @Valid UserUpdateRequest userUpdateRequest) {userService.update(userUpdateRequest);return ResponseEntity.ok().build();}@DeleteMapping@PreAuthorize("hasAnyRole('ROLE_ADMIN')")@ApiOperation("根据用户名删除用户")public ResponseEntity<Void> deleteUserByUserName(@RequestParam("username") String username) {userService.delete(username);return ResponseEntity.ok().build();}
}
相关文章:

【SpringSecurity】SpringSecurity+JWT实现登录
1. SpringSecurity介绍 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是为Java应用程序设计的,特别是那些基于Spring的应用程序。Spring Security是一个社区驱动的开源项目,它提供了全面的安全性解决方案,包括防…...

jmeter连接mysql
查询mysql数据库版本 SELECT VERSION(); 下载jmeter mysql 驱动jar包,版本低于mysql版本,放在jmeter的lib 路径下 MySQL :: Download MySQL Connector/J (Archived Versions) 添加JDBC Connection Configuration 填写 variable name 及数据库信息 注意…...

图书馆管理系统(三)基于jquery、ajax
任务3.4 借书还书页面 任务描述 这部分主要是制作借书还书的界面,这里我分别制作了两个网页分别用来借书和还书。此页面,也是通过获取books.txt内容然后添加到表格中,但是借还的操作没有添加到后端中去,只是一个简单的前端操作。…...

Nginx Location 配置块全解析与示例
Nginx Location 配置块全解析与示例 摘要: 本文深入探讨了 Nginx 中 location 配置块的功能、语法规则以及多种实际应用场景下的配置示例,旨在帮助读者全面理解并熟练掌握 location 配置块,以便在 Nginx 服务器配置中灵活运用,实…...

javalock(八)ReentrantReadWriteLock
ReentrantReadWriteLock: 同时实现了共享锁和排它锁。内部有一个sync,同时实现了tryAcquire/tryReleases、tryAcquireShared/tryReleasesShared,一共四个函数,然后ReentrantReadWriteLock内部还实现了一个ReadLock和一个WriteLock,…...

反射和设计模式
一、反射 1. 相关概念 (1) 类的对象:基于定义好的一个类,创建该类的实例,即利用 new 创建的实例就为类的对象。 (2) 类对象:类加载的产物,封装了一个类的所有信息 ( 包名、类名、父类、接口、属性、方法、构造方…...

双指针---和为s的两个数字
这里写自定义目录标题 题目链接问题分析代码解决执行用时 题目链接 购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。 问题分析 暴⼒解法,会超时 (两层…...

LLaMA-Factory 单卡3080*2 deepspeed zero3 微调Qwen2.5-7B-Instruct
环境安装 git clone https://gitcode.com/gh_mirrors/ll/LLaMA-Factory.git 下载模型 pip install modelscope modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /root/autodl-tmp/models/Qwen/Qwen2.5-7B-Instruct 微调 llamafactory-cli train \--st…...

智慧农业云平台与水肥一体化:道品科技引领农业现代化新潮流
在当今科技飞速发展的时代,农业也正经历着一场深刻的变革。智慧农业云平台和水肥一体化技术的出现,为农业生产带来了前所未有的机遇和挑战。 一、智慧农业云平台:农业生产的 “智慧大脑” 智慧农业云平台就像是农业生产的 “智慧大脑”&…...

241207_MindNLP中的大模型微调
241207_基于MindNLP的大模型高效微调 现在的大模型体量非常庞大,全量微调所需要的算力也特别庞大,个人开发者没有条件微调。参数量达到7B的模型才刚刚有涌现能力,但是我们要微调7B的模型的话,就需要328G的显存,至少需…...

MongoDB、Mongoose使用教程
文章目录 一:MongoDB 简介1.1 什么是 MongoDB1.2 特点1.3 与关系数据库的区别:1.4 资源链接: 二:安装 MongoDB2.1 安装前的准备2.2 安装、启动 MongoDB2.3 创建用户 MongoDB 三、连接四:MongoDB 基础操作4.1 库操作&am…...

单片机:实现控制步进电机正反转(附带源码)
1. 步进电机概述 步进电机(Step Motor)是一种能够将电能转换为机械能的电动机。其独特之处在于能够精确地控制转动角度,因此被广泛应用于需要精确控制的场合,如打印机、机器人、数控机床、自动化设备等。 步进电机的转动是以“步…...

安装指南|OpenCSG Starship上架GitHub Marketplace
在代码开发的日常中,你是否常常被以下问题困扰? 代码审查耗时太长,拖慢项目进度? 审查质量参差不齐,一些关键问题被遗漏? 复杂代码变更看不懂,审查者需要大量时间理解意图? 别担…...

Excel设置生日自动智能提醒,公式可直接套用!
大家好,我是小鱼。 今天跟大家分享一个WPS表格中根据出生日期,设置生日提醒,并且根据距离生日天数自动标记数据颜色。简单又实用,一个公式轻松搞定! 接下来我们先学习一下需要使用到的函数,然后再根据实例让…...

同步异步日志系统:前置知识
一、日志项目的介绍 1.1 为什么要有日志系统 1、⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题,可以借助日志系统来打印⼀些⽇志帮助开发⼈员解决问题 为什么不直接printf打印在屏幕上呢??因为现实中没有…...

微服务设计原则——功能设计
文章目录 1.ID生成2.数值精度3.DB操作4.性能测试5.版本兼容5.1 向旧兼容5.2 向新兼容 6.异步时序问题7.并发问题7.1 并发时序7.2 并发数据竞争 参考文献 1.ID生成 在分布式系统中,生成全局唯一ID是非常重要的需求,因为需要确保不同节点、服务或实例在并…...

低代码软件搭建自学的第一天——熟悉PyQt
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 学习计划第 1 步:环境搭建1.1 安装 Python1.2 安装 PyQt安装命令:验证安装: 第 2 步:PyQt 基础知识2.1 创建第一个窗…...

基于Python3编写的Golang程序多平台交叉编译自动化脚本
import argparse import os import shutil import sys from shutil import copy2from loguru import loggerclass GoBuild:"""一个用于构建跨平台执行文件的类。初始化函数,设置构建的主文件、生成的执行文件名称以及目标平台。:param f: 需要构建的…...

远程桌面连接
电脑A:使用机 电脑B:被控制的另一个 方法1: 在电脑B上操作 ①winr输入cmd进入命令行窗口,输入ipconfig查询本机地址 ②我的电脑/此电脑 右键点击“属性” ③选择屏幕右边“远程桌面” ④打开“启用远程桌面” ⑤打开设置&am…...

网络地址转换NAT
NAT(Network Address Translation) 方法于1994年提出。需要在专用网连接到因特网的路由器上安装NAT软件。装有NAT软件的路由器叫做NAT路由器,它至少有一个有效的外部全球地址IPG。 所有使用本地地址的主机在和外界通信时都要在NAT路由器上将其本地地址转换成外部全球…...

什么是CRM管理软件?CRM的基本概念、功能、选择标准、应用场景
什么是CRM管理软件? 嘿,大家好!今天咱们聊聊一个在现代企业管理中非常重要的工具——CRM管理软件。CRM是Customer Relationship Management(客户关系管理)的缩写,简单来说,它就是一个帮助企业和…...

Python编程常用的19个经典案例
Python 的简洁和强大使其成为许多开发者的首选语言。本文将介绍36个常用的Python经典代码案例。这些示例覆盖了基础语法、常见任务、以及一些高级功能。 1. 列表推导式 fizz_buzz_list ["FizzBuzz" if i % 15 0 else "Fizz" if i % 3 0 else "Buzz…...

【Unity基础】AudioSource 常用方法总结
在 Unity 中,AudioSource 组件用于控制音频的播放和管理。以下是常用的 AudioSource 控制方法及其说明。 1. 播放和暂停音频 Play():开始播放音频,如果是从暂停的地方继续播放,可以直接调用。Pause():暂停当前播放的…...

CSS系列(25)-- 滚动优化详解
前端技术探索系列:CSS 滚动优化详解 📜 致读者:探索流畅滚动的艺术 👋 前端开发者们, 今天我们将深入探讨 CSS 滚动优化,学习如何创建流畅、高性能的滚动体验。 平滑滚动 🚀 基础设置 /* …...

CST天线设计的六大核心特点:为天线分析提供完整解决方案!
CST Studio Suite 为天线设计提供了从最初的概念评估到最终的合规性测试所需的所有功能,确保天线设计在各种环境下实现稳定通信。这一套工具覆盖了所有重要的设计阶段,帮助设计师顺利完成从概念到成品的全过程。 下面我们来看一看CST电磁仿真中天线设计…...

Ubuntu下C语言操作kafka示例
目录 安装kafka: 安装librdkafka consumer Producer 测试运行 安装kafka: Ubuntu下Kafka安装及使用_ubuntu安装kafka-CSDN博客 安装librdkafka github地址:GitHub - confluentinc/librdkafka: The Apache Kafka C/C library $ apt in…...

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法
怎么将pdf中的某一个提取出来?传统上,我们可能通过手动截取屏幕或使用PDF阅读器的复制功能来提取信息,但这种方法往往不够精确,且无法保留原文档的排版和格式。此外,很多时候我们需要提取的内容可能涉及多个页面、多个…...

HTTP接口报错详解与解决 200,500,403,408,404
前言: 仅做学习记录,侵删 背景 当后端编写接口时,经常需要对接口使用ApiFox或者PostMan进行测试,此时就会出现各种各样的报错,一般都会包括报错编码:200,400,401等。这个状态码一般是服务器所返回的包含…...

监控IP频繁登录服务器脚本
该脚本的作用是监控IP登录失败次数,如果某个IP的登录失败次数超过设定的最大次数,则阻止该IP的进一步登录尝试。通过iptables防火墙阻止连接,当一个IP尝试登录次数超过5次时,iptables会阻止来自该IP的所有连接 #!/bin/bashfuncti…...

分布式链路追踪-03-Jaeger、Zipkin、skywalking 中的 span 是如何设计的?
开源项目 auto-log 自动日志输出 Jaeger、Zipkin 中的 spanId 是如何生成的? 在 Jaeger 和 Zipkin 这两个分布式跟踪系统中,Span ID 是通过不同的方法生成的。 下面分别介绍它们的生成方式: Jaeger 中的 Span ID 生成: 在 Ja…...