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

springboot+shiro+jwt 兼容session和token

最近和别的软件集成项目,需要提供给别人接口来进行数据传输,发现给他token后并不能访问我的接口,拿postman试了下还真是不行。检查代码发现项目的shiro配置是通过session会话来校验信息的 ,我之前一直是前后端自己写,用浏览器来调试的程序所以没发现这个问题。
浏览器请求头的cookie带着JESSIONID是可以正常访问接口的
在这里插入图片描述

那要和别的项目集成,他那边又不是通过浏览器,咋办呢,我这边改造吧,兼容token和session不就行了,下面直接贴改造后的完整代码。

pom加依赖

		<dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>2.4.2.1-RELEASE</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-all</artifactId><version>1.3.2</version></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.2.0</version></dependency>

1.JwtToken重写token类型

package com.mes.common.token;import com.mes.module.user.dto.SysUserDto;
import lombok.Data;
import org.apache.shiro.authc.HostAuthenticationToken;
import org.apache.shiro.authc.RememberMeAuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;@Data
public class JwtToken implements HostAuthenticationToken, RememberMeAuthenticationToken {private String token;private char[] password;private boolean rememberMe = false;private String host;public JwtToken(String token){this.token = token;}@Overridepublic String getHost() {return null;}@Overridepublic boolean isRememberMe() {return false;}@Overridepublic Object getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;}
}

2.自定义过滤器 JwtFilter

package com.mes.common.shiro;import com.alibaba.fastjson.JSON;
import com.auth0.jwt.interfaces.Claim;
import com.mes.common.token.JwtToken;
import com.mes.common.utils.JwtUtils;
import com.mes.common.utils.Result;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;/*** @Description 自定义过滤器* @Date 2021/8/18**/
public class JwtFilter extends AuthenticatingFilter {private static final Logger log = LoggerFactory.getLogger(JwtFilter.class);@Overrideprotected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {HttpServletRequest request = (HttpServletRequest) servletRequest;String token = request.getHeader("token");if (token == null){return null;}return new JwtToken(token);}/*** 拦截校验  没有登录的情况下会走此方法* @param servletRequest* @param servletResponse* @return* @throws Exception*/@Overrideprotected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;String token = request.getHeader("token");response.setContentType("application/json;charset=utf-8");response.setHeader("Access-Control-Allow-Credentials", "true");response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));response.setHeader("Access-Control-Allow-Methods", "GET,PUT,DELETE,UPDATE,OPTIONS");response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));Subject subject = getSubject(servletRequest,servletResponse);if (!subject.isAuthenticated()){// 未登录PrintWriter writer = response.getWriter();writer.print(JSON.toJSONString(new Result<>().setCode(402).setMsg("请先登录")));return false;}if (StringUtils.isEmpty(token)){PrintWriter writer = response.getWriter();writer.print(JSON.toJSONString(new Result<>().setCode(402).setMsg("请先登录")));return false;}else {// 校验jwttry {Map<String, Claim> claimMap = JwtUtils.verifyToken(token);} catch (Exception e) {e.printStackTrace();PrintWriter writer = response.getWriter();writer.write(JSON.toJSONString(new Result<>().setCode(402).setMsg("登录失效,请重新登录")));return false;}return executeLogin(servletRequest, servletResponse);}}@Overrideprotected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {HttpServletResponse httpServletResponse = (HttpServletResponse) response;Throwable throwable = e.getCause() == null ? e : e.getCause();Result result = new Result().err().setMsg(e.getMessage());String json = JSON.toJSONString(result);try {httpServletResponse.getWriter().print(json);} catch (IOException ioException) {}return false;}/*** 跨域支持* @param servletRequest* @param response* @return* @throws Exception*/@Overrideprotected boolean preHandle(ServletRequest servletRequest, ServletResponse response) throws Exception {HttpServletRequest httpRequest = WebUtils.toHttp(servletRequest);HttpServletResponse httpResponse = WebUtils.toHttp(response);if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpResponse.setHeader("Access-Control-Allow-Credentials", "true");httpResponse.setHeader("Access-control-Allow-Origin", httpRequest.getHeader("Origin"));httpResponse.setHeader("Access-Control-Allow-Methods", "GET,PUT,DELETE,UPDATE,OPTIONS");httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));System.out.println(httpRequest.getHeader("Origin"));System.out.println(httpRequest.getMethod());System.out.println(httpRequest.getHeader("Access-Control-Request-Headers"));httpResponse.setStatus(HttpStatus.OK.value());return false;}HttpServletRequest request = (HttpServletRequest) servletRequest;String token = request.getHeader("token");if (token != null) {try {
//                Map<String, Claim> claimMap = JwtUtils.verifyToken(token);
//                String authToken = claimMap.get("token").asString();JwtToken jwtToken = new JwtToken(token);Subject subject = SecurityUtils.getSubject();subject.login(jwtToken);return true;} catch (Exception e) {e.printStackTrace();log.error("token失效,请重新登录");response.getWriter().print(JSON.toJSONString(new Result<>().setCode(402).setMsg("token失效,请重新登录")));}}return false;}/*    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpRequest = WebUtils.toHttp(request);HttpServletResponse httpResponse = WebUtils.toHttp(response);if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpResponse.setHeader("Access-Control-Allow-Credentials", "true");httpResponse.setHeader("Access-control-Allow-Origin", httpRequest.getHeader("Origin"));httpResponse.setHeader("Access-Control-Allow-Methods", "GET,PUT,DELETE,UPDATE,OPTIONS");httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));System.out.println(httpRequest.getHeader("Origin"));System.out.println(httpRequest.getMethod());System.out.println(httpRequest.getHeader("Access-Control-Request-Headers"));httpResponse.setStatus(HttpStatus.OK.value());return false;}return super.preHandle(request, response);}*/}

3.配置过滤器 ShiroFilterRegisterConfig

package com.mes.common.config;import com.mes.common.shiro.JwtFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Description TODO* @Date 2021/8/19**/
@Configuration
public class ShiroFilterRegisterConfig {@Beanpublic FilterRegistrationBean shiroLoginFilteRegistration(JwtFilter filter) {FilterRegistrationBean registration = new FilterRegistrationBean(filter);registration.setEnabled(false);return registration;}
}

4. shiroConfig

package com.mes.common.config;

import com.baomidou.mybatisplus.extension.api.R;
import com.mes.common.constant.ExpTime;
import com.mes.common.realm.MyRealm;
import com.mes.common.shiro.JwtFilter;
import com.mes.common.shiro.MyCredentialsMatcher;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**

  • @Description shiro配置
  • @Date 2021/8/18
    **/
    @Configuration
    public class ShiroConfig {
    @Autowired
    private MyRealm myRealm;
    @Autowired
    private MyCredentialsMatcher myCredentialsMatcher;
    @Value(“ s p r i n g . r e d i s . h o s t " ) p r i v a t e S t r i n g r e d i s H o s t ; @ V a l u e ( " {spring.redis.host}") private String redisHost; @Value(" spring.redis.host")privateStringredisHost;@Value("{spring.redis.port}”)
    private Integer redisPort;
    @Value(“${spring.redis.timeout}”)
    private Integer redisTimeout;

// @Bean
// public DefaultWebSessionManager sessionManager(@Value(“${globalSessionTimeout:3600}”) long globalSessionTimeout, RedisManager c){
// DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// sessionManager.setSessionValidationSchedulerEnabled(true);
// sessionManager.setSessionIdUrlRewritingEnabled(false);
// sessionManager.setSessionValidationInterval(globalSessionTimeout * 1000);
// sessionManager.setGlobalSessionTimeout(globalSessionTimeout * 1000);
// sessionManager.setSessionDAO(redisSessionDAO©);
// return sessionManager;
// }
// @ConfigurationProperties(prefix=“spring.redis”)
// @Bean
// public RedisManager redisManager() {
// return new RedisManager();
// }
// @Bean
// public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {
// RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
// redisSessionDAO.setRedisManager(redisManager);
// return redisSessionDAO;
// }

// @Bean
// public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
//
// DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
// defaultAdvisorAutoProxyCreator.setUsePrefix(true);
//
// return defaultAdvisorAutoProxyCreator;
// }

@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(SessionManager sessionManager, RedisCacheManager redisCacheManager){DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();myRealm.setCredentialsMatcher(myCredentialsMatcher);defaultWebSecurityManager.setRealm(myRealm);

// defaultWebSecurityManager.setSessionManager(sessionManager);
defaultWebSecurityManager.setCacheManager(redisCacheManager);
return defaultWebSecurityManager;
}

@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager,JwtFilter jwtFilter){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

// JwtFilter jwtFilter = new JwtFilter();
Map<String, Filter> filterMap = new HashMap<>();
filterMap.put(“jwt”,jwtFilter);
shiroFilterFactoryBean.setFilters(filterMap);
Map<String,String> map = new LinkedHashMap<>();
map.put(“/sys/user/login”,“anon”);
map.put(“/swagger-ui.html**”, “anon”);
map.put(“/v2/api-docs”, “anon”);
map.put(“/swagger-resources/“, “anon”);
map.put(”/webjars/
”, “anon”);
map.put(“/img/“,“anon”);
map.put(”/fastdfs/
”,“anon”);
map.put(“/**”,“jwt”); //取消就不会拦截
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// shiroFilterFactoryBean.setLoginUrl(“http://192.168.18.17:3000”);
return shiroFilterFactoryBean;
}

@Bean
public JwtFilter getJwtFilter(){return new JwtFilter();
}/*** 配置shiro redisManager* 使用的是shiro-redis开源插件* @return*/
@Bean
public RedisManager redisManager() {RedisManager redisManager = new RedisManager();redisManager.setHost(redisHost);redisManager.setPort(redisPort);redisManager.setExpire(Math.toIntExact(ExpTime.expTime));// 配置缓存过期时间redisManager.setTimeout(redisTimeout);return redisManager;
}
@Bean
public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {

// RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager);
return redisSessionDAO;
}
/**
* shiro session的管理
*/
@Bean
public DefaultWebSessionManager redisSessionManager(RedisSessionDAO redisSessionDAO) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
@Bean
public RedisCacheManager redisCacheManager(RedisManager redisManager) {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager);
return redisCacheManager;
}

// @Bean
// public FilterRegistrationBean shiroLoginFilteRegistration(JwtFilter filter) {
// FilterRegistrationBean registration = new FilterRegistrationBean(filter);
// registration.setEnabled(false);
// return registration;
// }

}

在这里插入代码片

5.自定义认证逻辑 MyRealm

package com.mes.common.realm;

import com.auth0.jwt.interfaces.Claim;
import com.mes.common.token.JwtToken;
import com.mes.common.utils.JwtUtils;
import com.mes.module.user.dto.SysUserDto;
import com.mes.module.user.service.SysUserService;
import lombok.SneakyThrows;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**

  • @Description 授权

  • @Date 2021/8/18
    **/
    @Component
    public class MyRealm extends AuthorizingRealm {
    @Autowired
    private SysUserService sysUserService;

    @Override
    public boolean supports(AuthenticationToken token) {
    return token instanceof JwtToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    String username = (String) principalCollection.iterator().next();
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    return info;
    }

    @SneakyThrows
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    JwtToken jwtToken = (JwtToken) authenticationToken;
    String token = (String) jwtToken.getPrincipal();
    Map<String, Claim> claimMap = JwtUtils.verifyToken(token);
    String username = claimMap.get(“name”).asString();
    Map<String,Object> params = new HashMap<>();
    params.put(“username”, username);
    SysUserDto userDto = sysUserService.getOne(params);
    if (userDto == null){
    return null;
    }

// return new SimpleAuthenticationInfo(userDto,userDto.getPassword(),getName());
return new SimpleAuthenticationInfo(userDto,jwtToken,getName());
}
}

6.密码验证器

package com.mes.common.shiro;import com.mes.common.token.JwtToken;
import com.mes.common.utils.CommonsUtils;
import com.mes.module.user.dto.SysUserDto;
import com.mes.module.user.service.SysUserService;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;/*** @Description 密码验证器* @Date 2021/8/18**/
@Component
public class MyCredentialsMatcher extends SimpleCredentialsMatcher {@Autowiredprivate SysUserService sysUserService;@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {JwtToken jwtToken = (JwtToken) token;if (jwtToken.getPassword() == null){return true;}String inPassword = new String(jwtToken.getPassword());SysUserDto dto = (SysUserDto) info.getPrincipals();String username = dto.getUsername();String dbPassword = String.valueOf(info.getCredentials());Map<String,Object> params = new HashMap<>();params.put("username",username);SysUserDto dbUser = sysUserService.getOne(params);String salt = dbUser.getSalt();if (CommonsUtils.encryptPassword(inPassword,salt).equals(dbPassword)){return true;}else {return false;}}
}

相关文章:

springboot+shiro+jwt 兼容session和token

最近和别的软件集成项目&#xff0c;需要提供给别人接口来进行数据传输&#xff0c;发现给他token后并不能访问我的接口&#xff0c;拿postman试了下还真是不行。检查代码发现项目的shiro配置是通过session会话来校验信息的 &#xff0c;我之前一直是前后端自己写&#xff0c;用…...

CSS Display(显示)

CSS Display(显示) 概述 CSS&#xff08;层叠样式表&#xff09;中的display属性是控制元素如何显示的关键属性。它决定了元素的盒模型类型&#xff0c;即元素是块级元素、内联元素还是其他类型的元素。display属性对于网页布局和元素样式的控制至关重要。 基本用法 块级元…...

【PB案例学习笔记】-20制作一个超链接按钮

写在前面 这是PB案例学习笔记系列文章的第19篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…...

Django中使用下拉列表过滤HTML表格数据

在Django中&#xff0c;你可以使用下拉列表&#xff08;即选择框&#xff09;来过滤HTML表格中的数据。这通常涉及两个主要步骤&#xff1a;创建过滤表单和处理过滤逻辑。 创建过滤表单 首先&#xff0c;你需要创建一个表单&#xff0c;用于接收用户选择的过滤条件。这个表单可…...

Linux基础 (十五):TCP 协议特点和UDP协议

上一节&#xff0c;我们学习了TCP协议的服务器-客户端的编程流程以及对中间的过程进行了详细的讨论&#xff0c;那么&#xff0c;这一节&#xff0c;我们对于TCP协议的特点进行进一步的分析&#xff0c;这也是面试的重点和难点。 目录 一、TCP 协议特点 1.1 连接的建立与断…...

python替换word文件中的图片

python替换word文件中的图片 模拟鼠标键盘&#xff0c;截屏 import glob import os import timeimport pyautogui import pyautogui as p from PIL import ImageGrab from pynput.keyboard import Controller# -*- coding:utf-8 -*-directory ./directory1 ./outputfor f i…...

Servlet-01

文章目录 Servlet创建Servlet探究Servlet的生命周期 HttpServletWebServlet注解详解 重定向与请求转发ServletContextServletContext中的接口 HttpServletRequestHttpServletResponse状态码解释Cookie Servlet Q&#xff1a;它能做什么呢&#xff1f; A&#xff1a;我们可以通…...

C语言:链表

链表 介绍单向链表节点结构创建节点插入节点删除节点遍历链表尾部插入查找节点链表反转示例程序程序1程序2 介绍 链表是一种常见的数据结构&#xff0c;用于存储一系列线性数据。与数组不同&#xff0c;链表中的元素在内存中不必是连续存放的&#xff0c;而是通过指针将每个元…...

【git使用二】gitee远程仓库创建与本地git命令用法

目录 gitee介绍 管理者注册gitee账号 管理者在gitee网站上创建远程仓库 每个开发者安装git与基本配置 1.git的下载和安装 2.配置SSH公钥 3.开发者信息配置 git命令用法 gitee介绍 Gitee&#xff08;又称码云&#xff09;是一个基于Git的代码托管服务&#xff0c;由开源…...

明星百科大全PHP网站源码

源码介绍 明星百科大全网站源码&#xff0c;国内外明星娱乐音乐、新闻八卦、写真照片、相关影视作品等等的明星百科网站源码。 源码截图 源码下载 明星百科大全PHP网站源码...

白酒:茅台镇白酒的品鉴会与文化交流活动

茅台镇&#xff0c;这个位于中国贵州省的小镇&#xff0c;因其与众不同的自然环境和杰出的酿酒工艺而成为世界著名的白酒产区。云仓酒庄豪迈白酒作为茅台镇的品牌&#xff0c;积极参与各种品鉴会和文化交流活动&#xff0c;向世界展示了中国白酒的魅力和文化底蕴。 近年来&…...

python中列表结构在点云数据处理中用法

1、前言 Python中的列表&#xff08;list&#xff09;是一种可变的序列类型&#xff0c;用于存储集合数据。列表用途非常广泛&#xff0c;包括但不限于以下几个方面&#xff1a; 存储集合数据&#xff1a;列表用于存储一系列有序的元素&#xff0c;这些元素可以是任何数据类型&…...

土耳其(小亚细亚)历史上的各个阶段

一个国家的历史书写方式有两种&#xff0c;其一是按本国主体民族的渊源&#xff0c;其二是本国国土内发生的都属于本国史。一般来说&#xff0c;这两种方式相当程度上是重合的&#xff0c;但也有例外&#xff0c;比如本文要讲述的土耳其。 土耳其的国土并不辽阔&#xff0c;其…...

Windows下基于Frida查看内存基址和修改寄存器

使用Frida能够方便地获取到DLL基址&#xff0c;还能修改寄存器值。首先要通过任务管理器获得进程的PID&#xff0c;然后写Python脚本把Frida附加到这个PID进程&#xff0c;根据IDA分析出来的函数地址&#xff0c;HOOK到目标函数&#xff0c;修改寄存器的值&#xff0c;最终实现…...

2024中国网络安全产品用户调查报告(发布版)

自2020年始&#xff0c;人类进入了21世纪的第二个十年&#xff0c;全球进入了百年未有之大变局&#xff0c;新十年的开始即被新冠疫情逆转了全球化发展的历程&#xff0c;而至2022年3月俄乌战争又突然爆发&#xff0c;紧接着2023年7月“巴以冲突"皱起&#xff0c;世界快速…...

手写图片懒加载

参考来自前辈 Aidan路修远i 的文章面试官&#xff1a;请你手写一下&#xff01;懒加载 - 掘金 (juejin.cn) Hello.vue <template><div><!-- src里面为空&#xff0c;data-original里面写图片真正的url(此处省略) --><img src"" data-origina…...

大型语言模型(LLMs)的后门攻击和防御技术

大型语言模型&#xff08;LLMs&#xff09;通过训练在大量文本语料库上&#xff0c;展示了在多种自然语言处理&#xff08;NLP&#xff09;应用中取得最先进性能的能力。与基础语言模型相比&#xff0c;LLMs在少样本学习和零样本学习场景中取得了显著的性能提升&#xff0c;这得…...

力扣2594.修车的最少时间

力扣2594.修车的最少时间 二分答案 class Solution {public:long long repairCars(vector<int>& ranks, int cars) {ranges::sort(ranks);auto check [&](long long x) -> bool{long long res 0;for(auto v : ranks){long long k sqrt(x/v);res k;if(r…...

攻防演练之-成功的钓鱼邮件溯源

书接上文&#xff0c;《网络安全攻防演练风云》专栏之攻防演练之-网络安全产品大巡礼二&#xff0c;这里。 演练第一天并没有太大的波澜&#xff0c;白天的时间过得很快。夜色降临&#xff0c;攻防演练中心内的灯光依旧明亮。对于网络安全团队来说&#xff0c;夜晚和白天并没有…...

Gi标签管理

文章目录 前言理解标签创建标签操作标签总结 前言 理解标签 标签&#xff0c;可以理解为对某次commit的一次标识&#xff0c;相当于起起了一个别名。 例如&#xff0c;在项目发布某个版本时候&#xff0c;针对最后一次commit起一个v1.0这样的标签来标识里程碑的意义。 这有什…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...