spring boot中shiro使用自定义注解屏蔽接口鉴权

springboot+shiro前后端分离实现!! 本文章参考两位大佬写的博客完成的 https://blog.csdn.net/weixin_42236404/article/details/89319359 https://blog.csdn.net/qq_42109746/article/details/97102231 1、前言 1.1、唠嗑部分 学习shiro之前,建议先学习springsecurity,将两个框架进行对比的方式学习,会有更加深的印象。我之前写过一篇关于springsecurity的博客,你可以参考参考,个人感觉挺详细. 阅读详情

2022-09-28更新:

经过后续使用发现,使用了这种办法后会导致spring boot事务失效!ApiContext会在shiro初始化过滤器的时候扫描全部的controller,这会导致controller类和controller依赖的service被提前实例化(正常情况下应该是spring实例化所有的BeanPostProcessor后再实例化业务类),然后你的启动日志里边可能会出现如下信息:

022-09-28 08:47:52.151  INFO 34092 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'staffServiceImpl' of type [com.vanmesure.easyboot.program.service.impl.StaffServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

这样会导致此类不会被所有的BeanPostProcessor所代理,部分AOP会失效,这里我遇到的是@Transactional失效。最近实在太忙,后续会更新一下此问题的解决方案,以下内容现在只做参考,千万不要在生产环境中使用!

参考文章:解决:is not eligible for getting processed by all BeanPostProcessors_拾年一剑的博客-优快云博客

传统做法

spring boot整合shiro后,如果某些接口需要屏蔽鉴权的话(比如登录)接口,我们一般会这么做:

@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("authc", new CorsAuthorizationFilter());
       
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        /* -- 不去拦截的接口 --*/
        filterChainDefinitionMap.put("/statics/**", "anon");
        filterChainDefinitionMap.put("/auth/login", "anon");
        filterChainDefinitionMap.put("/auth/webLogin", "anon");
        filterChainDefinitionMap.put("/auth/loginPage", "anon");
        filterChainDefinitionMap.put("/projectTaskDefinition/list", "anon");
        /*需要拦截的接口*/
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        shiroFilterFactoryBean.getFilters().put("authc", new CorsAuthorizationFilter());
        return shiroFilterFactoryBean;
    }

但是这样做起来不是很优雅,每次编写完新的不需要鉴权的方法后需要再回来改这个地方,所以我就想能不能通过接口上加注解的方式来标识此接口是否需要屏蔽鉴权。

使用自定义注解屏蔽接口鉴权

1.首先定义一个自定义注解AnnoApi 

/**
 * 将此注解加到controller的方法上,即可将方法对应的接口地址自动添加到白名单中
 * anno是anonymous的简称
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoApi {
}

因为此注解只起到标识作用,所以不需要成员属性。

2.在启动时获取全部的接口路径

为了实现这个功能我单独写了一个ApiContxt类来处理

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class ApiContext {

    // 接口路径--方法 映射表
    private Map<String, Method> pathToMethodMap;

    private ApplicationContext applicationContext;

    /**
     * 扫描全部接口,并将其完整请求路径(不包含server.servlet.context-path)与方法的映射保存下来
     * 此方法默认所有打上@RequestMapping注解(或其派生注解)的类或方法都必须有至少一个访问路径,留空的话会抛出异常
     * @param applicationContext
     */
    public ApiContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        // 获取全部打了@RestController注解的类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(RestController.class);
        pathToMethodMap = new HashMap<>(beansWithAnnotation.size());
        for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
            Class<?> controller = entry.getValue().getClass();
            // 获取controller上的@RequestMapping注解
            RequestMapping controllerRequestMapping = controller.getAnnotation(RequestMapping.class);
            if (controllerRequestMapping != null) {
                Method[] controllerSubMethods = controller.getMethods();
                // 遍历controller下的所有方法,搜索所有加了@RequestMapping注解的方法
                for (Method method : controllerSubMethods) {
                    RequestMapping methodRequestionMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
                    if (methodRequestionMapping == null) {
                        continue;
                    }
                    // 将controller的访问路径和method的访问路径进行拼接,并保存到一个map中
                    for (String controllerPath : controllerRequestMapping.value()) {
                        if (!controllerPath.startsWith("/")) {
                            controllerPath = "/" + controllerPath;
                        }
                        for (String methodPath : methodRequestionMapping.value()) {
                            if (!methodPath.startsWith("/")) {
                                methodPath = "/" + methodPath;
                            }
                            // API完整的请求路径
                            String fullPath = controllerPath + methodPath;
                            pathToMethodMap.put(fullPath, method);
                        }
                    }
                }
            }
        }
    }

    public Map<String, Method> getPathToMethodMap() {
        return pathToMethodMap;
    }
}

大致意思就是将所有接口路径与对应方法的映射保存下来,供其他类使用。

细心的小伙伴可能会发现一个小问题,就是我在获取方法路径时取得是@RequestMapping注解的值,那么如果我的方法使用的是@PostMapping或@GetMappbing的话该怎么处理?

实际上上述代码是可以获取@GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping @RequestMapping这些注解的路径值的,在本文最后会简单说一下里边的原理,现在暂时认为是可以全部获取的就可以了。

3.配置shiro时使用ApiContext提取的接口信息配合自定义注解来动态添加白名单

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
                                                         ApiContext apiContext) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);


        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 无需拦截的接口
        for (Map.Entry<String, Method> entry : apiContext.getPathToMethodMap().entrySet()) {
            // 判断方法上是否存在AnnoApi注解
            AnnoApi annoApi = entry.getValue().getAnnotation(AnnoApi.class);
            if (annoApi != null) {
                // 接口地址是比较敏感的信息,将这个打印到日志里边不是很安全,可以考虑关掉
                log.info("添加白名单接口:" + entry.getKey());
                filterChainDefinitionMap.put(entry.getKey(), "anon");
            }
        }
        // 需要拦截的接口
        filterChainDefinitionMap.put("/**", "authc");
        // 使用自定义拦截器
        shiroFilterFactoryBean.getFilters().put("authc", new CorsAuthorizationFilter());
        return shiroFilterFactoryBean;
    }

循环遍历ApiContext提供的所有接口路径,然后判断每一个方法上是否有@AnnoApi标识,如果有的话就将其路径添加到白名单中,大功告成!

4.使用

使用方法很简单,在需要屏蔽鉴权的方法上添加上注解就可以了

拓展内容:关于spring中的派生注解

在上边第二步时我提到过这样一个问题

细心的小伙伴可能会发现一个小问题,就是我在获取方法路径时取得是@RequestMapping注解的值,那么如果我的方法使用的是@PostMapping或@GetMappbing的话该怎么处理?

为什么我获取@RequestMapping可以捎带着将@PostMapping或@GetMappbing一并获取了呢?

简单解释就是@PostMapping,@GetMapping等注解是@RequestMapping的派生注解。我们随便点开@PostMapping方法可以看到,这个注解上边被打上了@RequestMapping注解。派生注解是spring框架中的一个概念,与java本身无关,这里我们不去探究其原理(主要是我也不会),只知道@PostMapping与@RequestMapping实际上是有关联的就可以了。这个地方为了好理解也可以简单的认为@RequestMapping相当于是@PostMapping的父注解.

而spring框架中的工具类AnnotatedElementUtils中的findMergedAnnotation()可以获取一个方法上的某个特定注解,如果没有的话该方法会尝试查找已存在注解的父注解是否满足。所以下边这行代码在打了@PostMapping注解的方法上也是有效的了。

RequestMapping methodRequestionMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);

另外AnnotatedElementUtils.findMergedAnnotation()还对@AliasFor注解做了处理,简单说就是你的方法上打上了@PostMapping("add"),但是你拿到的父注解@RequestMapping中是没有“add”这个值的,@PostMapping的源码中通过@AliasFor注解指定了映射关系(如下图), 

然后AnnotatedElementUtils.findMergedAnnotation()方法对其进行了处理,所以我们才能在@RequestMapping中取到路径值。 

spring中还有个类似的工具方法,AnnotationUtils.findAnnotation(),也能获取父注解,但是这个方法并没有对@AliasFor注解做处理,所以拿到的父注解是没有属性值的。

```省略部分代码

String methodRequestPath = null; // 方法路径
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
if (requestMapping != null) {
    methodRequestPath = mapping.value()[0];
}

if (methodRequestPath == null) {
    GetMapping mapping = method.getAnnotation(GetMapping.class);
    if (mapping != null) {
        methodRequestPath = mapping.value()[0];
    }
}
if (methodRequestPath == null) {
    PostMapping mapping = method.getAnnotation(PostMapping.class);
    if (mapping != null) {
        methodRequestPath = mapping.value()[0];
    }
}
if (methodRequestPath == null) {
    PutMapping mapping = method.getAnnotation(PutMapping.class);
    if (mapping != null) {
        methodRequestPath = mapping.value()[0];
    }
}
if (methodRequestPath == null) {
    DeleteMapping mapping = method.getAnnotation(DeleteMapping.class);
    if (mapping != null) {
        methodRequestPath = mapping.value()[0];
    }
}
```省略部分代码

这个获取方法路径的方法将每个注解都判断了一下,然后取出路径,显然这个代码看着很难受。

后边修改为通过 AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);获取后就优雅多了。

首先需要明确的是,java中的注解是不可以继承的,所以spring中的派生注解应该是对注解继承的一个拓展。当然以上提到的注解继承、父注解等概念都是为了方便理解胡诌出来的,笔者并不保证其准确性。

若依框架深度解析:从企业级脚手架到二次开发实战指南 在Java企业级应用开发领域,Spring Boot作为核心框架,极大地简化了项目初始化和配置流程。其核心原理在于通过约定大于配置的理念,提供了一套标准化的项目结构和自动配置机制,从而显著提升开发效率。这一技术价值在于让开发者能够快速构建稳定、可扩展的后台管理系统,尤其适用于需要快速交付的中小型项目或内部运营平台。在实际应用场景中,开发者常基于此类框架进行二次开发,集成用户权限、菜单管理等通用模块,以快速搭建业务系统。本文聚焦于若依框架,这是一个基于Spring Boot的流行脚手架,深入探讨其核心架构、代 阅读详情

相关推荐

Ruoyi与Activiti深度集成:企业级审批流程解耦实战

工作流引擎是企业级应用开发中处理复杂业务流程的核心组件,它通过标准化的流程定义与执行引擎,实现了业务逻辑与流程控制的分离。其技术原理基于BPMN 2.0规范,以可视化的流程图驱动任务流转、状态管理和角色分配,从而提供清晰的历史追溯能力。在技术价值上,工作流引擎显著提升了流程的可维护性与开发效率,使业务变更能通过修改流程图快速响应,而非重构代码。其典型的应用场景包括OA系统、ERP、CRM中的各类审批流程,如公文流转、采购申请和费用报销。本文聚焦于将轻量级工作流引擎Activiti与快速开发平台Ruoyi进行

weixin_30236595的博客 349

shiro过滤器

ShiroFilterFactoryBean实现FactoryBean,说明他是shiroFilter工厂类,他是怎么初始化能很好的工作呢?该类的入口方法是createInstance(),该方法实现了几个功能 1、创建了一个过滤器管理类FilterChainManager,该类主要管理shiro里的过滤器,里面有两个重要的属性 1.1 filters:管理全部过滤器,包括默认的身份验证和权限验证的过滤器,这些过滤器分为两组,一组是认证过滤器,有anon,authenBasic,auchc,user,一

qq_35366466的博客 2153

[F2F] Spring相关

Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。

OxYGC 276

shiro动态配置过滤器

在创建AbstractShiroFilter(shiro过滤器)时,可以配置过滤器,比如: @Bean("shiroFilter") @DependsOn({"securityManager"}) public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBea

yaoct的博客 4311

全面解读Spring Cloud Zuul:从配置到优化的实战指南

在微服务架构中,API网关作为核心组件之一,承担着请求路由、负载均衡、安全认证等重要功能。Spring Cloud Zuul作为一款功能强大的API网关解决方案,得到了广泛应用。本文将深入探讨Spring Cloud Zuul的各项功能,从基础配置到工作原理,再到多层负载和应用优化,全面解析其在实际应用中的最佳实践与实用技巧,为开发者提供一站式指导,助力其打造高性能、高可用的微服务架构。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 169万+

基于SpringBoot+MyBatis+Layui+Shiro的权限管理系统实战项目

MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。与传统的 ORM 框架(如 Hibernate)不同,MyBatis 并不屏蔽 SQL,而是提供了一种灵活的方式来编写 SQL,并将结果映射到 Java 对象中。ORM(Object-Relational Mapping)即对象关系映射,是一种编程技术,用于将数据库中的数据与面向对象的语言(如 Java)中的对象进行自动转换。ORM 的核心思想是通过元数据将数据库表结构与 Java 类结构进行映射。

weixin_33068055的博客 731

Shiro中ShiroFilterFactoryBean的filterChainDefinitionMap配置

通过DefaultShiroFilterChainDefinition的add设置 DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition(); chain.addPathDefinition("/**", "jwt[permissive]");//jwt[permissive]就是一个普...

core815的专栏 1万+

Java OA系统源码包实战:从跑通到二次开发

企业办公自动化(OA)系统是Java开发中极具代表性的业务场景,常以SSM或Spring Boot框架实现,涉及Maven依赖管理、MySQL数据库设计、Tomcat部署等核心技术。其原理包括三层架构、权限拦截、数据库连接配置等,能帮助开发者理解真实项目的闭环流程。对于课程设计、毕业设计或面试准备,掌握OA系统源码的运行与改造方法具有实用价值。本文围绕源码包落地,聚焦环境配置、数据库导入、常见故障排查及二次开发技巧,为工程师提供一条从启动到改写的完整路径。

weixin_33946020的博客 283

spring boot自定义注解拦截接口

自定义注解,拦截接口请求 maven依赖管理 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 1 注解编写 @Documented @Target(ElementType.METHOD) @Retention(R

woniyu123的博客 955

SSM框架学生在线考试系统实战:从数据库设计到自动判分

在Web应用开发中,经典的三层架构(表示层、业务层、持久层)依然是很多业务系统的基础。SSM框架(Spring+SpringMVC+MyBatis)作为Java Web开发的经典组合,凭借其配置清晰、SQL可控等优势,在在线考试系统等业务场景中仍被广泛使用。如何设计合理的数据库表结构、实现高效的随机组卷、自动判分以及防作弊机制,是这类系统能否落地的关键。本文从实际项目经验出发,系统讲解了基于SSM框架构建学生在线考试系统的完整思路,包括核心模块划分、数据库设计、框架配置、业务逻辑实现、性能优化与部署踩坑记录

weixin_34409357的博客 306

SpringBoot AOP各种注解、自定义注解、鉴权使用案例(免费下载)

SpringBoot AOP各种注解、自定义注解、鉴权使用案例SpringBoot AOP各种注解、自定义注解、鉴权使用案例SpringBoot AOP各种注解、自定义注解、鉴权使用案例

深入解析Apache Shiro Web集成:从Filter链到安全实战

在Java Web应用开发中,权限控制与安全管理是构建企业级应用的核心基础。其实现原理通常基于Servlet Filter机制,通过在请求处理链中插入安全过滤器,实现对资源的访问控制。这项技术的核心价值在于提供声明式、非侵入式的安全解决方案,能够有效分离业务逻辑与安全检查代码,提升系统的可维护性和安全性。典型的应用场景包括后台管理系统、金融交易平台、企业内部应用等需要细粒度权限控制的Web项目。本文聚焦于Apache Shiro框架的Web集成机制,深入剖析其Filter链设计、Session管理以及安全配

weixin_30655569的博客 330

VFIO框架源码分析(二十一)- IOMMUFD 模式下的 DMA 映射机制

在虚拟化 I/O 的世界里,无论上层管理面(如对象模型、设备绑定)如何重构,数据面最核心的任务始终未变——DMA 映射(DMA Mapping)。即如何建立 Guest Physical Address (GPA) 到 Host Physical Address (HPA) 的映射关系,并将其填入 IOMMU 的硬件页表中,使得直通设备能够正确访问虚拟机内存。在 Legacy VFIO (Type1) 时代,这一职责由承担,它通过ioctl 操作 Container。

qq_36071928的博客 1009

swagger2使用小结

文章目录集成swagger21、引入依赖2、新建swagger2配置类3、访问地址相关注解@Api()、@ApiOperation():标注controller和method@ApiParam 用于方法、参数、字段上,请求属性@ApiModel、@ApiModelProperty:用于JavaBean和JavaBean的属性,请求和返回实体@ApiImplicitParams、@ApiImplicitParam:方法参数的说明@ApiResponses、@ApiResponse:方法返回值的说明@ApiIg

wlacode 2195

【实战】——零代码玩转Supervisely人像分割:从标注到模型训练的数据流水线搭建

本文详细介绍了如何零代码使用Supervisely平台搭建人像分割数据流水线,从智能标注到数据集格式转换,再到模型训练与优化。Supervisely的智能标注工具和协作功能大幅提升了人像分割项目的效率,特别适合视频会议背景虚化、虚拟试衣等应用场景。

weixin_30694389的博客 415

NXP S32K1 SPI驱动实战:从配置到DMA传输的完整流程解析

本文深入解析了NXP S32K1系列MCU的SPI驱动开发全流程。基于S32 Design Studio环境,详细阐述了从引脚配置、LPSPI模块初始化,到实现阻塞、中断及DMA三种传输模式的实战代码与配置要点。文章重点剖析了DMA传输的配置细节与性能优化,旨在帮助开发者构建高效、稳定的嵌入式SPI通信,并提供了SPI Flash驱动的综合案例。

blue42的博客 716

SpringBoot中service事务失效,service异常但事务不回滚,使用shiro在AuthorizingRealm子类中注入service导致其他service事务失效

最近测试接口,发现很多service的事务都没有生效.检查了事务注解,发现并不是网上常见的service相互注入和调用方法引起的问题. 针对其中一个方法进行了多次测试,发现该方法所在service虽然在其他地方被注入,但是该方法并没有被其他service调用过.更让人不解的是屏蔽掉该service所在的其中一个注入,事务就生效了. 参考了网上的一些常见解决办法,发现都没能解决.山重水复疑无路,...

xfxwancs的博客 807

YOLOv5+Astra Pro Plus深度相机:手把手教你实现实时目标检测(附ROS配置技巧)

本文详细介绍了如何将YOLOv5目标检测算法与Astra Pro Plus深度相机结合,构建实时目标检测系统。内容涵盖从硬件选型、环境搭建、ROS配置到YOLOv5部署与深度信息融合的完整流程,并分享了性能优化、避障算法等实战技巧,帮助开发者在机器人视觉等场景中快速实现智能感知应用。

weixin_28292761的博客 389

从在线考试系统理解Spring Boot+Vue+MySQL全栈开发与工程化思维

现代Web应用开发通常采用前后端分离架构,其核心在于通过RESTful API实现数据交互与业务解耦。在技术选型上,Spring Boot凭借其‘约定大于配置’的理念,极大地简化了Java后端服务的搭建与配置,成为快速构建微服务和API接口的主流框架。结合Vue.js这一渐进式前端框架,开发者可以高效构建响应式、组件化的用户界面,而MySQL作为成熟的关系型数据库,则为结构化数据提供了可靠的存储与事务支持。这种技术组合的价值在于平衡了开发效率、学习成本与系统稳定性,尤其适用于教育、企业管理等需要复杂表单交互

weixin_33924312的博客 296

Kinect V1结构光激光散斑原理及样式

   对 intel realsense F200 和 Kinect V1的coding pattern原理进行调研,知二者都使用light coding的方式,该方法与传统的coded structure light不同:传统编码结构光(空间编码,时间编码)最终使用的是三角测量的原理计算深度信息;light coding通过投射具有高度伪随机性的激光散斑,会随着不同距离变换不同

松间明月 1万+

从Jira到Notion:用Sequential Thinking重构你的敏捷工作流(含GPT-6配置模板)

本文探讨了如何利用Sequential Thinking这一AI认知模型,重构敏捷开发工作流。通过连接Jira、Notion等工具,构建动态依赖图谱,实现从被动响应到主动规划的转变。文章提供了详细的配置教程和GPT-6配置模板,帮助团队实现任务自动拆解、智能看板生成与跨平台协调,从而提升项目管理效率。

gpt4scribbler的博客 205

从零搭建任务管理模块:状态机设计、接口实现与工程实践

在后台管理系统中,任务管理是核心业务模块之一。其本质是通过明确的状态定义与流转规则,驱动任务从创建到归档的完整生命周期。状态机设计是其中关键,它能约束非法状态跳转,保证流程数据的严谨性。基于Spring Boot与MyBatis-Plus,开发者可以高效构建包含数据库设计、状态机校验、分页查询等能力的任务管理后端。本文结合实际业务场景,讲解状态枚举、流转校验、逻辑删除等工程实践,帮助开发者从零搭建可落地的任务管理流程,提升业务系统的可维护性与可追溯性。

weixin_30566111的博客 407

DDS协议--[第一章][概述]

Data Distribute Service,数据分发服务:数据分发服务(DDS™)是一个由对象管理组(OMG)发布的以数据为中心的中间件协议和API标准。DDS集成系统中的各个组件,提供低延迟数据连接、高可靠性以及高可扩展体系结构,以适应商业级物联网(IoT)应用程序的需求。

qq_41816308的博客 1152

JeecgBoot项目实战:从快速开发到可维护架构的演进之路

在软件开发领域,低代码平台和快速开发框架通过提供预设组件和代码生成能力,显著提升了项目初期构建效率。其核心原理在于将通用业务逻辑模块化,开发者通过配置和少量编码即可实现基础功能,从而快速验证业务模型。这种模式的技术价值在于降低开发门槛、缩短交付周期,尤其适用于后台管理系统等标准化场景。然而,随着项目规模增长和业务复杂度提升,初始的“能用”状态往往面临可维护性、性能及扩展性挑战。本文聚焦于JeecgBoot这一主流快速开发框架,探讨如何通过工程结构优化、权限体系深度定制和性能调优等实践,解决代码生成器二次加工

aizexi2652的博客 357

HTML爱心字母雨(完整代码)

HTML语言实现爱心字母雨的完整代码。

AIGC 2万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

上一篇: 虹软人脸识别java sdk使用报错:UnsatisfiedLinkError
下一篇: spring boot整合rocketmq报错:The consumer group[] has been created before,
傲娇的鲤鱼
博客等级 码龄10年 88粉丝 53原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值