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

1496 篇文章 10 订阅
1494 篇文章 14 订阅

传统做法 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.使用 使用方法很简单,在需要屏蔽鉴权的方法上添加上注解就可以了

编辑切换为居中

添加图片注释,不超过 140 字(可选)

拓展内容:关于spring中的派生注解 在上边第二步时我提到过这样一个问题 细心的小伙伴可能会发现一个小问题,就是我在获取方法路径时取得是@RequestMapping注解的值,那么如果我的方法使用的是@PostMapping或@GetMappbing的话该怎么处理? 为什么我获取@RequestMapping可以捎带着将@PostMapping或@GetMappbing一并获取了呢? 简单解释就是@PostMapping,@GetMapping等注解是@RequestMapping的派生注解。我们随便点开@PostMapping方法可以看到,这个注解上边被打上了@RequestMapping注解。派生注解是spring框架中的一个概念,与java本身无关,这里我们不去探究其原理(主要是我也不会),只知道@PostMapping与@RequestMapping实际上是有关联的就可以了。这个地方为了好理解也可以简单的认为@RequestMapping相当于是@PostMapping的父注解.

编辑切换为居中

添加图片注释,不超过 140 字(可选)

而spring框架中的工具类AnnotatedElementUtils中的findMergedAnnotation()可以获取一个方法上的某个特定注解,如果没有的话该方法会尝试查找已存在注解的父注解是否满足。所以下边这行代码在打了@PostMapping注解的方法上也是有效的了。 RequestMapping methodRequestionMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class); 另外 AnnotatedElementUtils.findMergedAnnotation()还对@AliasFor注解做了处理,简单说就是你的方法上打上了@PostMapping("add"),但是你拿到的父注解@RequestMapping中是没有“add”这个值的,@PostMapping的源码中通过@AliasFor注解指定了映射关系(如下图),

编辑切换为居中

添加图片注释,不超过 140 字(可选)

然后 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+JVM+分布式高并发,架构师指日可待

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Spring Boot 使用 shiro 配置自定义过滤器需要以下几个步骤: 1. 引入 shiro-spring-boot-starter 依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建自定义过滤器: ``` public class CustomFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { // 在这里实现自定义的过滤逻辑,返回 true 表示通过过滤器,返回 false 表示未通过过滤器 return true; } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { // 如果 isAccessAllowed 返回 false,则会进入到这里,可以在这里处理未通过过滤器的情况 return false; } } ``` 3. 配置 shiro 的 FilterChainDefinition: ``` @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); // 添加自定义过滤器,其 key 是过滤器名称,value 是该过滤器对应的路径 chainDefinition.addPathDefinition("/custom/**", "custom"); return chainDefinition; } ``` 4. 配置自定义过滤器: ``` @Bean("custom") public CustomFilter customFilter() { return new CustomFilter(); } ``` 5. 配置 shiro 的注解支持: ``` @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } ``` 完成以上步骤后,就可以在 Spring Boot 使用 shiro 配置自定义过滤器了。 ### 回答2: 在 Spring Boot 使用 Shiro 配置自定义过滤器分为三个步骤。 第一步,创建自定义过滤器类。可以通过实现 Shiro 的 Filter 接口来创建自定义过滤器。在自定义过滤器需要实现过滤规则,并对请求进行相应的处理。 第二步,配置 Shiro 过滤器链。在 Spring Boot 的配置类,通过创建 ShiroFilterFactoryBean 对象来配置 Shiro 的过滤器链。可以使用 Shiro 的 FilterChainDefinitionMap 对象来配置过滤器链,然后将该对象设置给 ShiroFilterFactoryBean。 第三步,启用 Shiro 过滤器。在 Spring Boot 的配置类,通过创建 DefaultFilterChainManager 对象,并将该对象设置给 ShiroFilterFactoryBean,启用自定义过滤器。 有了以上三步,就可以在 Spring Boot 使用 Shiro 配置自定义过滤器了。可以通过在自定义过滤器实现过滤规则来对请求进行拦截或处理,然后在 Shiro 过滤器链配置该过滤器,最后启用该过滤器。这样就可以实现对请求的自定义过滤器处理。 值得注意的是,在使用 Shiro 进行自定义过滤器配置时,需要保证 Shiro 的配置文件已经进行了相应的配置,包括认证和授权等相关配置。只有在正确配置的前提下,才能正确使用 Shiro 进行自定义过滤器的配置。 ### 回答3: 在Spring Boot使用Shiro配置自定义过滤器通常需要以下几个步骤: 1. 引入ShiroSpring Boot依赖。在pom.xml文件添加ShiroSpring Boot Starter依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 创建自定义过滤器类。可以通过实现`javax.servlet.Filter`接口或者继承`org.apache.shiro.web.servlet.OncePerRequestFilter`类来创建自定义过滤器。例如,创建一个名为`CustomFilter`的自定义过滤器类: ``` public class CustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 过滤器逻辑处理 // ... filterChain.doFilter(request, response); } } ``` 3. 在Shiro配置类注册自定义过滤器。创建一个Shiro配置类,并使用`@Configuration`注解标记为配置类。通过`@Bean`注解将自定义过滤器注册到Shiro的过滤器链。例如,在配置类`ShiroConfig`注册`CustomFilter`: ``` @Configuration public class ShiroConfig { @Bean public FilterRegistrationBean<CustomFilter> customFilterRegistrationBean() { FilterRegistrationBean<CustomFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new CustomFilter()); registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 过滤器执行顺序 registrationBean.addUrlPatterns("/*"); // 过滤器路径 return registrationBean; } } ``` 4. 配置Shiro的过滤规则。在Shiro配置文件,可以设置自定义过滤器的拦截规则。例如,在`shiro.ini`配置文件,设置自定义过滤器的拦截规则: ``` [urls] /** = customFilter // 对所有请求都使用自定义过滤器 ``` 通过以上步骤,在Spring Boot使用Shiro配置自定义过滤器就可以实现对特定请求的拦截和处理。在`CustomFilter`类的`doFilterInternal`方法编写自定义的过滤器逻辑,例如鉴权、权限验证等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值