微服务项目->在线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

分布式个人智能的崛起:OpenClaw 框架深度架构与工程实践

分布式个人智能的崛起:OpenClaw 框架深度架构与工程实践

随着人工智能技术从单一的“文本生成”向具备自主决策与行动能力的“智能代理(Agent)”系统演进,OpenClaw 作为一个开源、自托管且坚持“本地优先(Local-First)”哲学的框架,正成为工程化落地的核心工具。 OpenClaw 最初以 Clawdbot 和 Moltbot 之名活跃于开发者社区,后经品牌重塑正式定名。其核心愿景是构建一个运行在用户受控硬件上的**“全天候数字雇员”**。通过深度集成多种通信协议与系统级权限,它实现了从被动响应指令到主动提供服务的范式转移。 一、 核心架构:可靠的单点真值网关 OpenClaw 的设计避开了复杂且难以调试的分布式多代理集群,转而采用了一种高度可靠的网关(Gateway)模式。这种架构将长寿命的控制平面与瞬时性的代理运行时进行解耦,极大地提升了系统在复杂工程环境中的可维护性。 1. 序列化运行循环(Serialized Loop) 网关基于 Node.js 构建,作为消息接入、会话管理和工具执行的中央控制台。其最关键的工程约束在于**“序列化运行循环”:在特定会话中,系统严格遵循“接收输入、上下文装配、

By Ne0inhk
OpenClaw与企业即时通讯平台(钉钉/企业微信/飞书)的集成架构研究

OpenClaw与企业即时通讯平台(钉钉/企业微信/飞书)的集成架构研究

OpenClaw与企业即时通讯平台(钉钉/企业微信/飞书)的集成架构研究 前言 随着企业数字化转型的深入推进,即时通讯平台已成为企业内部沟通协作的核心工具。钉钉、企业微信、飞书作为国内三大主流企业即时通讯平台,不仅提供基础的即时消息功能,还集成了丰富的办公协作能力,包括日程管理、文档协作、审批流程、视频会议等。然而,这些平台在智能化、自动化方面的能力仍有提升空间,特别是在处理复杂业务逻辑、数据分析和个性化服务方面。 OpenClaw作为一款开源的本地AI助理框架,具备强大的插件化架构和灵活的扩展能力,能够与企业即时通讯平台深度集成,为企业提供智能化的消息处理、自动化工作流程和个性化服务。本文将深入研究OpenClaw与企业即时通讯平台的集成架构,分析钉钉、企业微信、飞书三大平台的API特性,设计统一的集成框架,并提供完整的实现方案和优化策略。 通过OpenClaw与企业即时通讯平台的集成,企业可以实现:智能客服机器人、自动化工作流、数据查询服务、通知推送、日程管理、审批自动化等功能,显著提升工作效率和协作体验。本文将从架构设计、技术实现、性能优化等多个维度,全面阐述OpenC

By Ne0inhk
置信传播(Belief Propagation, BP)译码算法(公式推导+代码,超详细)

置信传播(Belief Propagation, BP)译码算法(公式推导+代码,超详细)

一、理论基础 此部分参考资料: LDPC码(一种前向纠错码):基础 & 译码算法 - 知乎 1.1 概述 置信传播(Belief Propagation,BP)算法,在编码理论中又常被称为和积算法(Sum-Product Algorithm,SPA),是一种在概率图模型上进行统计推断的消息传递机制,其核心思想是通过在Tanner图的变量节点(VN)与校验节点(CN)之间迭代传递置信度信息,逐步逼近最大后验概率(MAP)。 BP算法由朱迪亚·珀尔(Judea Pearl)于1982年提出,最初用于贝叶斯网络和马尔可夫随机场的概率推断,后被引入通信领域并发展为LDPC码的核心译码方法。但BP译码算法不是只能用于LDPC码,理论上可以用于任何拥有因子图(Factor Graph)表示的码型,包括所有的线性分组码,甚至卷积码(Turbo码)。 Turbo码:Turbo码的迭代译码本质上就是在两个分量码的因子图之间交换信息,属于BP算法的一个特例。Polar码(极化码)

By Ne0inhk