微服务项目->在线oj系统(Java-Spring)----6.0

微服务项目->在线oj系统(Java-Spring)----6.0

创建token

在oj-common-security中引入依赖

创建jwt⼯具类:

package com.bite.common.security.utils; import com.bite.common.core.comtains.JwtContains; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.HashMap; import java.util.Map; public class JwtUtils { /** * 生成令牌 * * @param claims 数据 * @param secret 密钥 * @return 令牌 */ public static String createToken(Map<String, Object> claims, String secret) { String token = Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret) .compact(); return token; } /** * 从令牌中获取数据 * * @param token 令牌 * @param secret 密钥 * @return 数据 */ public static Claims parseToken(String token, String secret) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } } 

createToken

----根据所传的claims和secret创建一个token

注意:这里的secret需要保密随机,不能硬编码,可以定期更换

(所以可以采用nacos来配置secret,这个后面细讲)

硬编码(Hard Coding)是指在程序代码中直接嵌入固定数值、字符串或逻辑,而非通过变量、配置文件或外部输入来动态获取的编程方式。这种做法会导致代码灵活性降低,维护成本增加。以下是关于硬编码的详细解析:

-

里面的这个SignatureAlgorithm.HS512是创建所使用的算法

使用例子:

首先创建一个token,这里由于是实例,所以密钥随便即可

 public static void main(String[] args) { Map<String,Object> claim=new HashMap<>(); claim.put("userid",123456); System.out.println(createToken(claim,"ddwdwdwdw5dfw5")); } 

获取到到token后通过JwtUtil的parseToken进行解码(需要token和secret)

 public static void main(String[] args) { String token="eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyaWQiOjEyMzQ1Nn0.F0jIBBpUgyhUPZSSrKVHOBBatVhFEIRSWh6LRjPXC5kdJ3TsFOzByhBryx_nV9nLlFpeE-4ZvOESQMWKn9nDjw"; Claims claims=parseToken("eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyaWQiOjEyMzQ1Nn0.F0jIBBpUgyhUPZSSrKVHOBBatVhFEIRSWh6LRjPXC5kdJ3TsFOzByhBryx_nV9nLlFpeE-4ZvOESQMWKn9nDjw","ddwdwdwdw5dfw5"); System.out.println(claims); } 

目的

在OJ-modules中引入oj-common-security

创建常量类

 public class CacheConstants { public final static String LOGIN_TOKEN_KEY = "login_tokens:"; public final static long EXPIRATION = 720; } 
public class JwtContains { public static final String LOGIN_USER_ID="userId"; public static final String LOGIN_USER_KEY="userKey"; } 

定义登录用户

@Data public class LoginUser { private Integer identity; //普通用户1 管理员2 } 

引入依赖

 <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool-all.version}</version> </dependency> <dependency> <groupId>com.bite</groupId> <artifactId>oj-common-redis</artifactId> <version>${oj-common-redis.version}</version> <scope>compile</scope> </dependency>

定义TokenService类

@Service public class TokenService { @Autowired private RedisService redisService; /** * 创建令牌 */ public String createToken(Long userId,String secret,Integer identity) { String userKey = UUID.fastUUID().toString(); // Jwt存储信息 Map<String, Object> claim = new HashMap<String, Object>(); claim.put(JwtContains.LOGIN_USER_ID,userId); claim.put(JwtContains.LOGIN_USER_KEY,userKey); String token=JwtUtils.createToken(claim,secret); String key=CacheConstants.LOGIN_TOKEN_KEY+userKey; LoginUser loginUser=new LoginUser(); loginUser.setIdentity(UserIdentity.ADMIN.getCode()); redisService.setCacheObject(key, loginUser, CacheConstants.EXPIRATION, TimeUnit.MINUTES); return token; } } 

核心代码详解:

首先我们通过UUID来获取一个唯一的userKey

创建一个claim,存放着用户的userId以及刚刚创建的userKey;

这个是调用方传入通过nacos得到的密钥,我们需要传给createToken

我们通过claim,secret来生成一个token.

然后我们创建一个变量key(其实就是一个常量+userKey);

然后将key存入redis中,value是用户身份

对login接口中Service部分进行修改

对oj-common-security项目进行修改

身份认证

引入依赖

填入白名单

创建一个白名单类,从nacos中去获取哪些是白名单中的url

@Configuration//配置类 @RefreshScope//实时刷新,使nacos上面的改变立刻生效 @ConfigurationProperties(prefix = "security.ignore") // public class IgnoreWhiteProperties { /** * 放⾏⽩名单配置,⽹关不校验此处的⽩名单 */ private List<String> whites = new ArrayList<>(); public List<String> getWhites() { //拿到nacos配置里面的白名单 return whites; } public void setWhites(List<String> whites) { this.whites = whites; } }

修改nacos

自定义过滤器

import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import com.bite.common.core.comtains.CacheConstants; import com.bite.common.core.comtains.HttpConstants; import com.bite.common.core.domain.R; import com.bite.common.core.enums.ResultCode; import com.bite.common.core.enums.UserIdentity; import com.bite.common.redis.service.RedisService; import com.bite.common.security.model.LoginUser; import com.bite.common.security.utils.JwtUtils; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.List; /** * ⽹关鉴权 * */ @Slf4j @Component public class AuthFilter implements GlobalFilter, Ordered { //实现GlobalFilter相当于实现了一个自定义过滤器 //实现Order是为什么以后有多个过滤器的时候进行先后排序 // 排除过滤的 uri ⽩名单地址,在nacos⾃⾏添加 @Autowired private IgnoreWhiteProperties ignoreWhite; @Value("${jwt.secret}") private String secret; @Autowired private RedisService redisService; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); //获取请求 String url = request.getURI().getPath(); // 跳过不需要验证的路径 if (matches(url, ignoreWhite.getWhites())) { return chain.filter(exchange); //如果是白名单里面的,则跳过身份认证 } //从http请求头中获取token String token = getToken(request); //判断token是否为空,如果为空说明以前未登录或者登录超市; if (StrUtil.isEmpty(token)) { return unauthorizedResponse(exchange, "令牌不能为空"); } Claims claims; try { claims = JwtUtils.parseToken(token, secret); //获取令牌中信息 解析payload中信息 if (claims == null) { return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); } } catch (Exception e) { return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); } String userKey = JwtUtils.getUserKey(claims); //获取jwt中的key boolean isLogin = redisService.hasKey(getTokenKey(userKey));//用得到的key去Redis中查找,如果找不到说明过期了 if (!isLogin) { return unauthorizedResponse(exchange, "登录状态已过期"); } String userid = JwtUtils.getUserId(claims); //判断jwt中的信息是否完整 if (StrUtil.isEmpty(userid)) { return unauthorizedResponse(exchange, "令牌验证失败"); } //c端用户只能请求C端功能,B端用户只能请求管理端功能 LoginUser user = redisService.getCacheObject(getTokenKey(userKey), LoginUser.class);//通过key去Redis里面查找value(存着用户是管理员还是用户) if (url.contains(HttpConstants.SYSTEM_URL_PREFIX) && !UserIdentity.ADMIN.getCode().equals(user.getIdentity())) { return unauthorizedResponse(exchange, "令牌验证失败"); } if (url.contains(HttpConstants.FRIEND_URL_PREFIX) && !UserIdentity.ORDINARY.getCode().equals(user.getIdentity())) { return unauthorizedResponse(exchange, "令牌验证失败"); } return chain.filter(exchange); } /** * 查找指定url是否匹配指定匹配规则链表中的任意⼀个字符串 * * @param url 指定url * @param patternList 需要检查的匹配规则链表 * @return 是否匹配 */ private boolean matches(String url, List<String> patternList) { if (StrUtil.isEmpty(url) || CollectionUtils.isEmpty(patternList)) { return false; } for (String pattern : patternList) { //从白名单里面分别列出白名单里面的访问地址 //判断有没有匹配的 if (isMatch(pattern, url)) { return true; } } return false; } /** * 判断url是否与规则匹配 * 匹配规则中: * ? 表⽰单个字符; * * 表⽰⼀层路径内的任意字符串,不可跨层级; * ** 表⽰任意层路径; * * @param pattern 匹配规则 * @param url 需要匹配的url * @return 是否匹配 */ private boolean isMatch(String pattern, String url) { AntPathMatcher matcher = new AntPathMatcher(); //将pattern作为匹配规则 return matcher.match(pattern, url); } /** * 获取缓存key */ private String getTokenKey(String token) { return CacheConstants.LOGIN_TOKEN_KEY + token; } /** * 从请求头中获取请求token */ private String getToken(ServerHttpRequest request) { String token = request.getHeaders().getFirst(HttpConstants.AUTHENTICATION); // 如果前端设置了令牌前缀,则裁剪掉前缀 if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) { token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY); } return token; } private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) { log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); return webFluxResponseWriter(exchange.getResponse(), msg, ResultCode.FAILED_UNAUTHORIZED.getCode()); } //拼装webflux模型响应 private Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String msg, int code) { response.setStatusCode(HttpStatus.OK); response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); R<?> result = R.fail(code, msg); DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); return response.writeWith(Mono.just(dataBuffer)); } @Override public int getOrder() { return -200; //定义的值越小越靠前执行 } } 

不懂的地方看代码注释

这里为什么不抛出异常,然后之前全局异常捕获?

这是因为@RestControllerAdvice只对springweb起作用,而springcloud gataway属于webflux

RedisConfig修改需要增加@AutoConfigureBefore(RedisAutoConfiguration.class)注解否则会报错如下:

让spring优先注册我们这个RedisTemplate

配置secret

配置redis

然后我们测试一下:

Read more

Nano Banana进行AI绘画中文总是糊?一招可重新渲染,清晰到可直接汇报

Nano Banana进行AI绘画中文总是糊?一招可重新渲染,清晰到可直接汇报

文章目录 * 1. 为什么 Nano Banana 生成的中文经常不清晰? * 2. 解决思路:Nano Banana + Seedream 4.5 的两段式工作流 * 3. 实战:先用 Nano Banana 生成架构图(中文会糊) * 4. 部署 Personal LLM API,并配置 Seedream 4.5 * 5. 用 Cherry Studio 配置已部署的 LLM 接口 * 6. 关键一步:用 Seedream 4.5 对“中文文字重新渲染” * 7. 效果对比:字清晰、无错位、图形保持不变

By Ne0inhk

【GitHub项目推荐--Video2Robot:从视频到机器人动作的端到端生成管道】⭐

简介 Video2Robot 是由AIM-Intelligence开发的开源项目,是一个端到端的管道系统,能够将视频或文本提示转换为机器人可执行的运动序列。在机器人技术、动画制作和虚拟现实快速发展的今天,如何让机器人执行自然、流畅的人类动作成为关键挑战。传统方法需要专业动画师手动设计动作,或通过复杂的运动捕捉系统,过程耗时耗力且成本高昂。Video2Robot应运而生,通过整合先进的视频生成、人体姿态提取和运动重定向技术,实现了从简单描述到机器人动作的自动化转换。 核心价值: * 自动化流程:将复杂的手动设计过程自动化,显著提高效率 * 自然动作生成:基于真实人类动作生成自然流畅的机器人运动 * 多模态输入:支持文本提示、现有视频、图像参考等多种输入方式 * 广泛兼容性:支持多种主流机器人平台,包括Unitree、Booster等 项目定位:Video2Robot填补了自然语言/视频到机器人动作转换的技术空白。与需要专业设备和复杂流程的传统运动捕捉系统不同,该项目通过软件管道实现了低成本、高效率的动作生成。项目特别注重易用性和可扩展性,通过模块化设计支持不同组件的替换和

By Ne0inhk
IROS 2025 无人机及集群 学习与控制合集

IROS 2025 无人机及集群 学习与控制合集

无人机系统面临着复杂多变的环境挑战,如动态障碍物、风扰、系统故障等,传统的基于精确模型的控制方法往往难以适应这些不确定性。为此,学习与控制相结合的智能方法成为研究热点。 刚刚过去的IROS 2025上,涌现了多篇聚焦单体无人机和多智能体的结合学习与控制的论文,体现了其在提升系统自主性与适应性方面的探索。NOKOV度量动作捕捉系统为研究提供高精度位姿“真值”,助力现实实验验证。同时在多智能体研究中支持控制算法的闭环执行。 · 单体无人机 一、基于学习的动态模型——在状态空间层面进行学习,获得系统的动力学模型 论文:PI-WAN: A Physics-Informed Wind-Adaptive Network for Quadrotor Dynamics Prediction in Unknown Environments (IROS 2025) 作者:Mengyun Wang, Bo Wang, Yifeng Niu and Chang Wang 国防科技大学 牛轶峰老师团队针对四旋翼在未知风扰环境中建模不准、数据驱动方法泛化差的问题,提出物理信息风自适应网络,将物理

By Ne0inhk