SpringBootWeb登录认证
JWT令牌
JSON Web Token JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).https://jwt.io/JSON Web Token(JWT)是一种基于JSON的开放标准(RFC 7519),用于在各方之间安全地传输信息。以下是对JWT进行详细介绍:
- JWT的工作原理:JWT的工作原理基于令牌(Token)的生成和验证两个主要过程。一旦用户通过身份验证,服务器会生成一个包含用户信息的JWT。这个令牌由三部分组成:标头(Header)、有效载荷(Payload)以及签名(Signature)。标头通常包含令牌的类型和所使用的签名算法;有效载荷则包含了实际的数据声明,如发行人、到期时间、主题、用户等信息;签名是为了确保令牌在传输过程中不被篡改。
- JWT的优点:JWT提供了无状态、可扩展且安全的认证解决方案,特别适用于分布式环境和移动应用。由于JWT自包含所有用户信息,服务端不需要存储session信息,从而减轻了服务器的压力。同时,JWT可以跨域使用,支持单点登录(SSO),并且不依赖于Cookie,因此也避免了CSRF攻击的风险。
- JWT的缺点:尽管JWT具有许多优点,但它也存在一些缺点。例如,一旦JWT被窃取,攻击者就可以访问所有的用户资源,直到令牌过期。此外,如果JWT未加密,通过某些手段可能会被解码并修改其内容。
在考虑使用JWT时,还需要注意以下几个方面:
- 安全性:虽然JWT可以加密并签名以保证数据的安全性和完整性,但默认情况下JWT是不加密的。因此,在使用JWT传输敏感信息时,应采取额外的安全措施,如使用HTTPS协议进行传输。
- 有效期:为了减少被盗用的风险,JWT的有效期不宜设置过长。对于一些重要操作,应要求用户频繁进行身份验证。
- 存储:虽然JWT通常存储在客户端,但也可以选择存储在服务器端,这取决于特定的应用场景和安全需求。
综上所述,JSON Web Token(JWT)是一个功能强大的工具,它提供了一种轻量级的解决方案,用于在现代Web应用程序中安全地处理身份验证和信息交换。通过了解JWT的工作原理、优缺点以及正确的使用方法,开发者可以有效地利用这一技术来提高应用的安全性和用户体验。
依赖配置
pom.xml引入JWT依赖
<!-- JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
生成JWT令牌
public void genJwt(){Map<String,Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","Tom");String jwt = Jwts.builder().setClaims(claims) //自定义内容(载荷) .signWith(SignatureAlgorithm.HS256, "密钥") //签名算法 .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期 .compact();System.out.println(jwt);
}
输出的结果就是生成的JWT令牌,,通过英文的点分割对三个部分进行分割。可以打开JWT官网进行校验。
校验JWT令牌
public void parseJwt(){Claims claims = Jwts.parser().setSigningKey("密钥")//指定签名密钥.parseClaimsJws("JWT令牌内容").getBody();System.out.println(claims);
}
将JWT令牌封装成工具类
public class JwtUtils {private static String signKey = "wfit";private static Long expire = 43200000L;/*** 生成JWT令牌* @param claims JWT第二部分负载 payload 中存储的内容* @return*/public static String generateJwt(Map<String, Object> claims){String jwt = Jwts.builder()//添加内容荷载.addClaims(claims)//签名算法.signWith(SignatureAlgorithm.HS256, signKey)//令牌时间.setExpiration(new Date(System.currentTimeMillis() + expire))//转化字符串.compact();return jwt;}/*** 解析JWT令牌* @param jwt JWT令牌* @return JWT第二部分负载 payload 中存储的内容*/public static Claims parseJWT(String jwt){Claims claims = Jwts.parser()//签名密钥.setSigningKey(signKey)//JWT令牌.parseClaimsJws(jwt).getBody();return claims;}
}
过滤器 Filter
过滤器(Filter)是Java Web应用中的一个重要组件,它主要用于请求到达Servlet之前或响应返回客户端之前对请求和响应进行处理。具体分析如下:
- 基本介绍:过滤器可以对进入的应用请求进行预处理,也可以对出去的响应进行后处理。这种机制使得开发人员可以在请求到达目标之前,以及响应被发送给客户端之前,执行一些特定的操作。例如,可以用于实现权限验证、请求内容的转换、日志记录等。
- 过滤器原理:过滤器的工作原理基于一种链式结构,即过滤器链。当一个请求到达时,它会按照配置的顺序通过每个过滤器。每个过滤器都可以修改请求或响应,或者决定是否将请求/响应传递到链中的下一个过滤器或目标资源。
- 过滤器接口:在Java中,创建过滤器需要实现
javax.servlet.Filter
接口,该接口定义了init()
,doFilter()
, 和destroy()
三个方法。init()
方法在过滤器初始化时调用,doFilter()
方法用于实际的过滤操作,而destroy()
方法在过滤器销毁前调用。 - 使用过滤器:要使用过滤器,首先需要创建一个实现了Filter接口的Java类,然后通过注解
@WebFilter
指定过滤规则,或者在web.xml
文件中配置过滤器及其拦截路径。过滤器的具体逻辑实现在doFilter()
方法中完成。 - 配置过滤器:过滤器的配置可以通过两种方式完成:一是使用
@WebFilter
注解直接在过滤器类上指定拦截路径;二是在项目的web.xml
文件中配置过滤器及其拦截的URL模式。这两种方式都允许灵活地控制哪些请求应该被拦截和处理。 - 过滤器生命周期:过滤器的生命周期由Web容器管理。当Web应用启动时,过滤器被初始化;接收到请求时,执行
doFilter()
方法;在Web应用关闭时,执行destroy()
方法以释放资源。 - 多个Filter的执行顺序:当使用多个过滤器时,它们的执行顺序非常重要。这个顺序可以通过在
web.xml
中配置的顺序或使用@WebFilter
注解指定的顺序参数来确定。正确的执行顺序确保了过滤器能够按照预期的方式工作。
此外,在开发过程中,需要注意以下几点:
- 正确配置:确保过滤器及其拦截路径正确配置,避免拦截不应该被拦截的请求。
- 性能考虑:虽然过滤器提供了强大的功能,但不当使用可能会影响应用性能。应尽量减少过滤器的数量,并优化过滤逻辑。
- 线程安全问题:由于过滤器对象可能在多线程环境下被并发访问,因此需要确保过滤器的实现是线程安全的。
总的来说,过滤器是Java Web开发中一个非常强大且灵活的工具,能够帮助开发者实现许多高级功能,提高代码的模块化和重用性。通过合理使用过滤器,可以极大地提升Web应用的安全性、效率和可维护性。
启动类注解
@ServletComponentScan//识别service
@SpringBootApplication
public class WebManagementApplication {public static void main(String[] args) {SpringApplication.run(TliasWebManagementApplication.class, args);}}
只有添加了@ServletComponentScan,Spring才能正常使用Filter。
设计拦截器实现Filter接口,重新其中方法即可。
执行流程
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {@Override //初始化方法, 只调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println("init 初始化方法执行了");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("DemoFilter 放行前逻辑.....");//放行请求filterChain.doFilter(servletRequest,servletResponse);System.out.println("DemoFilter 放行后逻辑.....");}@Override //销毁方法, 只调用一次public void destroy() {System.out.println("destroy 销毁方法执行了");}
}
拦截路径参数
拦截路径 | urlPatterns值 | 含义 |
---|---|---|
拦截具体路径 | /login | 只有访问 /login 路径时,才会被拦截 |
目录拦截 | /emps/* | 访问/emps下的所有资源,都会被拦截 |
拦截所有 | /* | 访问所有资源,都会被拦截 |
使用拦截器和JWT令牌实现登录认证案例
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//类型请求对象HttpServletRequest ser= (HttpServletRequest) servletRequest;HttpServletResponse serp= (HttpServletResponse) servletResponse;//获取请求URLString url = ser.getRequestURL().toString();log.info("url:{}",url);//判断url是否包含login关键字,如果有就放行if (url.contains("login")){log.info("登录操作,放行...");filterChain.doFilter(servletRequest,servletResponse);return;}//获取请求头中的令牌String jwt = ser.getHeader("token");//判断令牌是否存在//否if (!StringUtils.hasLength(jwt)){log.info("token为空,未登录");Result error = Result.error("NOT_LOGIN");//手动将对象转换为JSON --->阿里巴巴fastJSONString jsonString = JSONObject.toJSONString(error);//将JSON返回给浏览器serp.getWriter().write(jsonString);return;}//是try {JwtUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info("令牌解析失败");Result error = Result.error("NOT_LOGIN");//手动将对象转换为JSON --->阿里巴巴fastJSONString jsonString = JSONObject.toJSONString(error);//将JSON返回给浏览器serp.getWriter().write(jsonString);return;}//放行log.info("令牌合法,放行");filterChain.doFilter(servletRequest,servletResponse);}}
拦截器Interceptor
拦截器(Interceptor)是一种用于在控制器处理请求之前或之后执行某些操作的机制。以下将深入介绍拦截器的相关信息:
拦截器的基本概念:
- 拦截器(Interceptor)在Spring框架中,主要用于对Controller层的处理方法进行拦截,即可以在方法执行前后加入自定义的操作。拦截器与过滤器(Filter)相比,过滤器更广泛地用于请求和响应的预处理和后处理,而拦截器则更加关注于具体的处理器(Controller)方法。
拦截器的实现原理:
- 拦截器通常通过代理方式或反射机制实现。在Spring MVC应用中,拦截器通过动态代理来实现对Controller方法的增强。当一个HTTP请求到达时,如果配置了拦截器,那么这个请求在被目标Controller处理之前,会先经过一系列拦截器的处理。
拦截器的主要作用:
- 日志记录:记录请求信息,包括请求的URL、IP地址、时间等,便于监控和日志分析。
- 权限检查:例如检查用户是否已登录,是否有权访问某个特定的资源或服务。
- 性能监控:计算请求的处理时间,帮助开发者优化应用性能。
- 通用行为增强:如提取用户信息放入请求中,方便后续流程使用;或者根据不同的用户设置不同的主题和语言。
拦截器的自定义实现:
- 要创建自定义的拦截器,需要实现
HandlerInterceptor
接口或继承HandlerInterceptorAdapter
类,并重写preHandle()
,postHandle()
, 和afterCompletion()
三个方法。这些方法分别在请求处理前、视图渲染前、以及整个请求结束后被调用。
拦截器的使用场景举例:
- 在电商平台中,可以使用拦截器来检查用户是否登录,如果没有登录则重定向到登录页面。
- 对于需要计费的API,可以利用拦截器来计算调用次数和时长,从而实现计费功能。
- 在内容管理系统中,使用拦截器来限制只有特定角色的用户才能访问某些管理功能。
此外,考虑到拦截器的强大功能及其灵活性,开发者在使用时应注意以下几点:
- 正确配置:确保拦截器及其拦截规则正确配置,避免影响正常的业务流程。
- 性能考虑:虽然拦截器提供了强大的功能,但不当使用可能会影响应用性能。应尽量减少拦截器的数量,并优化其逻辑。
- 线程安全问题:由于拦截器对象可能在多线程环境下被并发访问,因此需要确保拦截器的实现是线程安全的。
综上所述,拦截器在Spring MVC应用中扮演着至关重要的角色,它不仅能够增强方法处理的能力,还能提高代码的模块化和重用性。通过合理使用拦截器,可以极大地提升Web应用的安全性、效率和可维护性。
自定义拦截器
需要实现HandlerInterceptor接口,并重写其所有方法
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {//目标资源方法执行前执行。 返回true:放行 返回false:不放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle .... ");return true; //true表示放行}//目标资源方法执行后执行@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle ... ");}//视图渲染完毕后执行,最后执行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion .... ");}
}
注意:
preHandle方法:目标资源方法执行前执行。 返回true:放行 返回false:不放行
postHandle方法:目标资源方法执行后执行
afterCompletion方法:视图渲染完毕后执行,最后执行
注册配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {//自定义的拦截器对象@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)}
}
拦截路径
拦截路径 | 含义 | 举例 |
---|---|---|
/* | 一级路径 | 能匹配/depts,/emps,/login,不能匹配 /depts/1 |
/** | 任意级路径 | 能匹配/depts,/depts/1,/depts/1/2 |
/depts/* | /depts下的一级路径 | 能匹配/depts/1,不能匹配/depts/1/2,/depts |
/depts/** | /depts下的任意级路径 | 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1 |
@Configuration
public class WebConfig implements WebMvcConfigurer {//拦截器对象@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求).excludePathPatterns("/login");//设置不拦截的请求路径}
}
使用过滤器和JWT令牌实现登录认证案例
注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**")//拦截什么样的路径.excludePathPatterns("/login");//不拦截什么路径}
}
定义拦截器
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {@Override //目标资源方法运行前运行,true放行public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String url = request.getRequestURL().toString();log.info("拦截路径:{}",url);String token = request.getHeader("token");if (!StringUtils.hasLength(token)){log.info("token为空");Result error = Result.error("NOT_LOGIN");String s = JSONObject.toJSONString(error);response.getWriter().write(s);return false;}try {JwtUtils.parseJWT(token);} catch (Exception e) {log.info("令牌有误");response.getWriter().write(JSONObject.toJSONString(Result.success("NOT_LOGIN")));return false;}log.info("令牌正确,执行登录");return true;}@Override //目标资源方法运行后运行public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Override //视图渲染完毕后执行public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
扩展——执行流程
-
当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。
-
Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。
-
当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行
preHandle()
方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)。 -
在controller当中的方法执行完毕之后,再回过来执行
postHandle()
这个方法以及afterCompletion()
方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。
相关文章:

SpringBootWeb登录认证
JWT令牌 JSON Web Token JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signatur…...

vim编辑器的使用
删除当前行及以后的行 使用vim编辑器打开文件 vim xxx.txt删除指定行及后面所有行,光标定位到要删除的第一行,比如:删除第10行以后的数据,光标定位到11行,然后在命令模式下按dG...

深入理解Linux网络总结
1、内核如何接收网络包 1.1 RingBuffer到底是什么,RingBuffer为什么会丢包? 问:RingBuffer到底存在那一块,是如何被使用到的,真的就只是一个环形队列吗?RingBuffer内存是预先分配好的,还是随着…...

Python冷知识
Python作为一种广泛使用的编程语言,有许多功能和特性可能不为初学者或普通用户所熟知。以下是一些相对冷门但有趣的Python知识: 魔术方法:Python中有一些特殊的方法,通常以双下划线__开头和结尾,被称为魔术方法(或特殊方法)。例如,__init__用于初始化对象,__str__返回…...

Redis之内存管理过期、淘汰机制
1.Redis内存管理 我们的redis是一个内存型数据库,我们的数据也都是放在内存中的,内存是有限的空间,当数据满了之后,我们要怎么样继续保证redis的可用性呢?我们就需要采取点管理措施和机制来保证我们redis的可用性。 在redis.co…...

金融科技赋能跨境支付:便捷与安全并驾齐驱
一、引言 在全球经济一体化的背景下,跨境支付作为国际贸易和金融活动的重要组成部分,正迎来金融科技浪潮的洗礼。金融科技以其独特的创新性和颠覆性,正在重塑跨境支付市场的格局,使其更加便捷、高效且安全。本文旨在探讨金融科技如何助力跨境支付,实现便捷与安全并存,并…...

【康耐视国产案例】智能AI相机:深度解析DataMan 380大视野高速AI读码硬实力
随着读码器技术的不断更新迭代,大视野高速应用成为当前工业读码领域的关键发展方向。客户对大视野高速读码器的需求源于其能显著减少生产成本并提升工作效率。然而,大视野应用场景往往伴随着对多个条码的读取需求,这无疑增加了算法的处理负担…...

SQL实验 带函数查询和综合查询
一、实验目的 1.掌握Management Studio的使用。 2.掌握带函数查询和综合查询的使用。 二、实验内容及要求 1.统计年龄大于30岁的学生的人数。 --统计年龄大于30岁的学生的人数。SELECT COUNT(*) AS 人数FROM StudentWHERE (datepart(yea…...

【前端每日基础】day34——HTTP和HTTPS
HTTP(Hypertext Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)是互联网通信协议,用于在Web浏览器和Web服务器之间传输数据。以下是对HTTP和HTTPS的详细介绍: HTTP(Hypertext…...

go mongo 唯一索引创建
1. 登录mongo,创建数据库 mongosh -u $username -p $password use test 2. 查看集合索引 db.$collection_name.getIndexes() 为不存在的集合创建字段唯一索引 package mainimport ("context""fmt""log""time""go…...

微信小程序如何进行页面跳转
微信小程序中的页面跳转可以通过多种方式实现,以下是几种主要的跳转方式及其详细解释: wx.navigateTo 功能:保留当前页面,跳转到应用内的某个页面。特点: 可以在新页面使用wx.navigateBack返回原页面。每跳转一个新页…...

信息标记形式 (XML, JSON, YAML)
文章目录 🖥️介绍🖥️三种形式🏷️XML (Extensible Markup Language)🔖规范🔖注释🔖举例🔖其他 🏷️JSON (JavaScript Object Notation)🔖规范🔖注释&#x…...

C语言:学生成绩管理系统(含源代码)
一.功能 二.源代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NUM 100 typedef struct {char no[30];char name[10];char sex[10];char phone[20];float cyuyan;float computer;float datastruct; } *student, student1;typ…...

MySQL 导出导入的101个坑
最近接到一个业务自行运维的MySQL库迁移至标准化环境的需求,库不大,迁移方式也很简单,由开发用myqldump导出数据、DBA导入,但迁移过程坎坷十足,记录一下遇到的各项报错及后续迁移注意事项。 一、 概要 空间问题源与目…...

OpenCv之简单的人脸识别项目(人脸提取页面)
人脸识别 准备五、人脸提取页面1.导入所需的包2.设置窗口2.1定义窗口外观和大小2.2设置窗口背景2.2.1设置背景图片2.2.2创建label控件 3.定义单人脸提取脚本4.定义多人脸提取脚本5.创建一个退出对话框6.按钮设计6.1单人脸提取按钮6.2多人脸提取按钮6.3返回按钮 7.定义关键函数8…...

linux 内核映像差异介绍:vmlinux、zImage、zbImage、image、uImage等
一、背景 Linux内核是整个Linux操作系统的核心部分,它是一个负责与硬件直接交互的软件层,并且提供多种服务和接口,让用户程序能够方便地使用硬件资源。 当我们编译自定义内核时,可以将其生成为以下内核映像之一:vmli…...

【Linux-INPUT输入的子系统】
Linux-INPUT输入的子系统 ■ input 子系统简介■ input 驱动编写流程■ 事件类型 ■ ■ input 子系统简介 input 子系统就是管理输入的子系统, input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点 …...

密码加密及验证
目录 为什么需要加密? 密码算法分类 对称密码算法 非对称密码算法 摘要算法 DigestUtils MD5在线解密工具原理 实现用户密码加密 代码实现 为什么需要加密? 在MySQL数据库中,我们常常需要对用户密码、身份证号、手机号码等敏感信息进…...

找出字符串中出现最多次数的字符以及出现的次数
str.charAt(i) 是JavaScript中获取字符串中特定位置字符的方法,表示获取当前的字符。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…...

如何看待央行买卖长期国债?
央行远比大家想象中的要渴求货币宽松。 引子 今年以来,有不少关于“央行买卖长期国债”的讨论,前些时候关注点在“买”,最近关注点在“卖”。 然而,市场上的讨论采用了十分粗糙和松散的“自然语言”,所以࿰…...

MATLAB算法实战应用案例精讲-【数模应用】Turf组合模型(附MATLAB、python和R语言代码实现)
目录 几个高频面试题目 如何以最小的成本覆盖到最大的消费群体? 应用场景 TURF举例...

android源码下载编译模拟器运行
安卓aosp源码下载,编译,模拟器运行 virtualbox7 安装ubuntu20.04,ubuntu22.04 编译android aosp 源码可以,但是模拟器跑不了,哪个版本都是要么黑屏,要么整个vbox虚拟机闪退。解决方案使用vmware17 ##拯救…...

Golang:Sirupsen/logrus是一个日志库
Sirupsen/logrus是一个日志库 文档 https://github.com/Sirupsen/logrus 安装 go get github.com/sirupsen/logrus代码示例 package mainimport ("github.com/sirupsen/logrus" )func main() {var log logrus.New()log.Trace("Something very low level.&…...

Android Studio插件开发 - Dora SDK的IDE插件
IDE插件开发简介 Android Studio是一种常用的集成开发环境(IDE),用于开发Android应用程序。它提供了许多功能和工具,可以帮助开发人员更轻松地构建和调试Android应用程序。 如果你想开发Android Studio插件,以下是一…...

【mybatis】缓存
一级缓存和二级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从一级缓存中直接获取,不会从数据库重新查询。一级缓存默认是开启 使一级缓存失效的四种情况: 11.1…...

自定义类型:结构体类型
在学习完指针相关的知识后将进入到c语言中又一大重点——自定义类型,在之前学习操作符以及指针时我们对自定义类型中的结构体类型有了初步的了解,学习了结构体类型的创建以及如何创建结构体变量,还有结构体成员操作符的使用,现在我…...

C++对象移动
在某些情况下,对象拷贝后就立即被销毁了,这时利用新标准(C11)提供的对象移动而非拷贝将大幅提升性能. 1.右值引用 为了支持移动操作,c11新增了一种引用 - 右值引用(rvalue reference)。这种引用必须指向右值,使用&&声明。 右值引用只能引用临时变量或常量值. 右值引用…...

“华为杯”第十三届中国研究生 数学建模竞赛-E题:粮食最低收购价政策问题研究(续)
目录 4.3 问题三:粮食价格的特殊规律性模型 4.3.1 分析和建模 4.3.2 求解和结果...

(一)django目录介绍
1、生成django项目,得到的目录如下 manage.py:命令行工具,内置多种方式与项目进行交互。在命令提示符窗口下,将路径切换到项目并输入python manage.py help,可以查看该工具的指令信息。 默认的数据库工具,sqlite 在…...

leetcode5 最长回文子串
给你一个字符串 s,找到 s 中最长的 回文 子串。 示例 1: 输入:s "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。示例 2: 输入:s "cbbd" 输…...