范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

如何利用自定义注解放行SpringSecurity项目的接口

  来源:blog.csdn.net/weixin_45089791/article/details/118890274
  在实际项目中使用到了springsecurity作为安全框架,我们会遇到需要放行一些接口,使其能匿名访问的业务需求。但是每当需要当需要放行时,都需要在security的配置类中进行修改,感觉非常的不优雅。
  例如这样:
  所以想通过自定义一个注解,来进行接口匿名访问。在实现需求前,我们先了解一下security的两种方行思路。
  第一种就是在  configure(WebSecurity web)  方法中配置放行,像下面这样:@Override public void configure(WebSecurity web) throws Exception {     web.ignoring().antMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico", "/verifyCode"); }
  第二种方式是在  configure(HttpSecurity http)  方法中进行配置:@Override protected void configure(HttpSecurity httpSecurity) throws Exception {  httpSecurity     .authorizeRequests()           .antMatchers("/hello").permitAll()           .anyRequest().authenticated() }
  两种方式最大的区别在于,第一种方式是不走 Spring Security 过滤器链,而第二种方式是走 Spring Security 过滤器链,在过滤器链中,被请求放行。
  在我们使用 Spring Security 的时候,有的资源可以使用第一种方式额外放行,不需要验证,例如前端页面的静态资源,就可以按照第一种方式配置放行。
  有的资源放行,则必须使用第二种方式,例如登录接口。大家知道,登录接口也是必须要暴露出来的,不需要登录就能访问到的,但是我们却不能将登录接口用第一种方式暴露出来,登录请求必须要走 Spring Security 过滤器链,因为在这个过程中,还有其他事情要做,具体的登录流程想了解的可以自行百度。 了解完了security的两种放行策略后,我们开始实现
  首先创建一个自定义注解 @Target({ElementType.METHOD}) //注解放置的目标位置,METHOD是可注解在方法级别上 @Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行 @Documented //生成文档 public @interface IgnoreAuth { }
  这里说明一下, @Target({ElementType.METHOD})  我的实现方式,注解只能标记在带有@RequestMapping  注解的方法上。具体为什么下面的实现方式看完就懂了。
  接下来创建一个security的配置类SecurityConfig并继承 WebSecurityConfigurerAdapter  @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {      @Autowired     private RequestMappingHandlerMapping requestMappingHandlerMapping;      /**      * @ description: 使用这种方式放行的接口,不走 Spring Security 过滤器链,      *                无法通过 SecurityContextHolder 获取到登录用户信息的,      *                因为它一开始没经过 SecurityContextPersistenceFilter 过滤器链。      * @ dateTime: 2021/7/19 10:22      */     @Override     public void configure(WebSecurity web) throws Exception {         WebSecurity and = web.ignoring().and();         Map handlerMethods = requestMappingHandlerMapping.getHandlerMethods();         handlerMethods.forEach((info, method) -> {             // 带IgnoreAuth注解的方法直接放行             if (StringUtils.isNotNull(method.getMethodAnnotation(IgnoreAuth.class))) {                 // 根据请求类型做不同的处理                 info.getMethodsCondition().getMethods().forEach(requestMethod -> {                     switch (requestMethod) {                         case GET:                             // getPatternsCondition得到请求url数组,遍历处理                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 // 放行                                 and.ignoring().antMatchers(HttpMethod.GET, pattern);                             });                             break;                         case POST:                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 and.ignoring().antMatchers(HttpMethod.POST, pattern);                             });                             break;                         case DELETE:                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 and.ignoring().antMatchers(HttpMethod.DELETE, pattern);                             });                             break;                         case PUT:                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 and.ignoring().antMatchers(HttpMethod.PUT, pattern);                             });                             break;                         default:                             break;                     }                 });             }         });     } }
  在这里使用Spring为我们提供的 RequestMappingHandlerMapping  类,我们可以通过requestMappingHandlerMapping.getHandlerMethods();  获取到所有的RequestMappingInfo  信息。
  以下是源码部分,可不看,看了可以加深理解
  这里简单说一下 RequestMappingHandlerMapping  的工作流程,便于理解。我们通过翻看源码
  继承关系如上图所示。
  AbstractHandlerMethodMapping  实现了InitializingBean   接口public interface InitializingBean {     void afterPropertiesSet() throws Exception; }
  AbstractHandlerMethodMapping  类中通过afterPropertiesSet  方法调用initHandlerMethods  进行初始化 public void afterPropertiesSet() {         this.initHandlerMethods();     }      protected void initHandlerMethods() {         String[] var1 = this.getCandidateBeanNames();         int var2 = var1.length;          for(int var3 = 0; var3 < var2; ++var3) {             String beanName = var1[var3];             if (!beanName.startsWith("scopedTarget.")) {                 this.processCandidateBean(beanName);             }         }          this.handlerMethodsInitialized(this.getHandlerMethods());     }
  再调用 processCandidateBean  方法: protected void processCandidateBean(String beanName) {         Class beanType = null;          try {             beanType = this.obtainApplicationContext().getType(beanName);         } catch (Throwable var4) {             if (this.logger.isTraceEnabled()) {                 this.logger.trace("Could not resolve type for bean "" + beanName + """, var4);             }         }          if (beanType != null && this.isHandler(beanType)) {             this.detectHandlerMethods(beanName);         }      }
  通过调用方法中的isHandler方法是不是 requestHandler  方法,可以看到源码是通过RequestMapping  ,Controller 注解进行判断的。protected boolean isHandler(Class<?> beanType) {         return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);     }
  判断通过后,调用 detectHandlerMethods   方法将handler注册到HandlerMethod的缓存中。protected void detectHandlerMethods(Object handler) {         Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();         if (handlerType != null) {             Class<?> userType = ClassUtils.getUserClass(handlerType);             Map methods = MethodIntrospector.selectMethods(userType, (method) -> {                 try {                     return this.getMappingForMethod(method, userType);                 } catch (Throwable var4) {                     throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);                 }             });             if (this.logger.isTraceEnabled()) {                 this.logger.trace(this.formatMappings(userType, methods));             }              methods.forEach((method, mapping) -> {                 Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);                 this.registerHandlerMethod(handler, invocableMethod, mapping);             });         }      }
  通过 registerHandlerMethod  方法将handler放到private final Map mappingLookup = new LinkedHashMap();  map中。
  而 requestMappingHandlerMapping.getHandlerMethods()  方法就是获取所有的HandlerMapping。public Map getHandlerMethods() {     this.mappingRegistry.acquireReadLock();      Map var1;     try {         var1 = Collections.unmodifiableMap(this.mappingRegistry.getMappings());     } finally {         this.mappingRegistry.releaseReadLock();     }      return var1; }
  最后就是对map进行遍历,判断是否带有 IgnoreAuth.class  注解,然后针对不同的请求方式进行放行。handlerMethods.forEach((info, method) -> {             // 带IgnoreAuth注解的方法直接放行             if (StringUtils.isNotNull(method.getMethodAnnotation(IgnoreAuth.class))) {                 // 根据请求类型做不同的处理                 info.getMethodsCondition().getMethods().forEach(requestMethod -> {                     switch (requestMethod) {                         case GET:                             // getPatternsCondition得到请求url数组,遍历处理                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 // 放行                                 and.ignoring().antMatchers(HttpMethod.GET, pattern);                             });                             break;                         case POST:                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 and.ignoring().antMatchers(HttpMethod.POST, pattern);                             });                             break;                         case DELETE:                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 and.ignoring().antMatchers(HttpMethod.DELETE, pattern);                             });                             break;                         case PUT:                             info.getPatternsCondition().getPatterns().forEach(pattern -> {                                 and.ignoring().antMatchers(HttpMethod.PUT, pattern);                             });                             break;                         default:                             break;                     }                 });             }         });
  看到这里就能理解我最开始的强调的需标记在带有 @RequestMapping  注解的方法上。我这里使用到的是configure(WebSecurity web)  的放行方式。它是不走security的过滤链,是无法通过 SecurityContextHolder   获取到登录用户信息的,这点问题是需要注意的。

阿里云早期未意识到Apachelog4j2漏洞的严重性将强化漏洞管理品玩12月23日讯,阿里云发布关于开源社区Apachelog4j2漏洞情况的说明。Log4j2是开源社区阿帕奇(Apache)旗下的开源日志组件,被全世界企业和组织广泛应用于各种业过度收集个人信息?中消协20款APP存在不同程度问题中国消费者协会今天(14日)发布50款APP账号注销及自动化推荐退订测评报告显示,在是否可以顺利注销APP账号方面,50款APP中有20款APP存在不同程度问题,占总排查比例的40四方支付聚合支付聚合支付解决什么问题1支付渠道碎片化这是什么意思呢?简单来说就是支付公司比较多,每家都有自己的产品,这就造成用户在使用时的不方便,比如客户要付款,走不同的渠道就会打开不同的APP,小学人工智能普及课程建设与实施机器学习正在重塑人类的胜任力(competence),人工智能已经成为现代社会一种非常重要的变革力量。国外针对K12所做的人工智能教育指南中曾指出,在不久的将来,几乎每个人都需要对年末家电涨价潮来临还想出手的你不要错过这最后的机会相信最近不少人都发现一件事,那就是家电涨价了。无论是各地线下卖场还是电商线上平台,也不管是大家电还是小家电,价格均有不同程度的上浮,而之所以出现这种年末涨价的现象,其实很大程度与久大家买过最满意的镜头是什么?最滿意的意的应该是隹能70200,性价比也是不容质疑的,价格,质量,满意就是最大的性价比。值得信赖。佳能70200f2。8三代,和尼康105f1。4。因为佳能和尼康我都有,说起这两麦芒10的价格很有勇气,居然卖到2299,但只给到天玑720处理器发布会介绍价格的时候,说了这样的一句话,这么优秀的产品它的价格是多少呢,8128G内存是2299元,我当时真的是笑了,的确,这手机真的太优秀了。对于这款手机,我不想多废话,直接来看高中生须知未来10年,这些专业可能消失人工智能的发展让越来越多的行业减少了人力的输出,那么在未来十年里哪些专业是最容易被人工智能取代的呢?向学霸进军为大家盘点下未来最容易被取代和不容易被取代的职业,供参考。什么类型的工不吹不黑,2021这三款手机最值得入手,颜值高性能强转眼间2021年马上过去,抛开接下来要发布的小米12等不谈,今年已经发布的新机,虽然说各个品牌没有什么大的创新,但是能在这日益激烈的手机市场中打造出属于自己的产品,相信每一个品牌都天玑9000性能曝光,高通拱手相让高端市场联发科和高通两家公司作为科技行业两大巨头,也可以勉强称之为友商,两家公司之间一直处于比赛的状态,你方唱罢我方登场。而在最新出炉的2021年全球半导体企业市值100强排行榜中高通排名三轮车老年代步车旧电池旧车为什么很值钱?行内人说了实话请您在阅读前,先点击上面的关注。感谢您的支持,我们将为您带来更多有价值的内容。新能源战略家出过一期旧的电动车的市场参考价。当时很多三轮车老年代步车的车主就按捺不住了,问新能源战略家
全球最奢侈的二十大名表排名(下)手表,现在做为计时工具的作用已不复存在了。但手表已经成为了装饰和身份的象征(尤其是男人),尤其是机械手表,在现在已成为了手腕上的艺术品。而世界名表的价格惊人,其制作精良,造型极具艺均衡器(EQ)对耳机耳塞听感的改善的实践基础篇一提到均衡器EQ,发烧友多数人的反应就是增加失真的玩意!在很多烧友的眼里,只要是hifi,只要是高保真,就是不能EQ的。原因很简单,器材已经高保真的,你的EQ只能失真而不可能让原来全球最奢侈的二十大名表排名手表,现在做为计时工具的作用已不复存在了。但手表已经成为了装饰和身份的象征(尤其是男人),尤其是机械手表,在现在已成为了手腕上的艺术品。而世界名表的价格惊人,其制作精良,造型极具艺阿丽塔,罗德里格斯的酷和卡梅隆的特效阿丽塔上影后,大家注意力都放到了卡梅隆的身上。确实,有了卡神的加入(那怕只是监制),影片的质量都有了很大的保证。电影中的特效让人眼前一亮,片中阿丽塔栩栩如生的股肤,细致到每一个毛孔为什么当前区块链技术难以运用到银行系统随着近年来区块链技术及DLT(分布式账本)的发展与应用,银行和其他金融机构正在努力的将这些技术带入到业务模式种。实际上许多这些机构已经通过概念验证(PoC)把这些商业模式运用到市场从此Facebook不存在,新品牌Meta诞生10月29日凌晨,Facebook更名为Meta的消息刷爆全网,成为人们争相讨论的话题。从此Facebook不存在,新品牌Meta诞生早在10月20日,就有知情人士透露Facebo为港珠澳大桥谱写的交响曲梦桥及唱片介绍目前世界上没有任何一个国家可以做到这件事情55公里跨海大桥7公里海底隧道,面对海面异常复杂的汹涌波涛,历时13年建造,桥面铺装相当于98个足球场!作曲家方岽清骄傲地说。这座完全由中灰度向美国SEC提出比特币现货ETF申请灰度公司创始人昨日发推特,称灰度已通过NYSEArca向美国SEC提交了19b4文件,申请将GBTC转换为比特币现货ETF。灰度向美国SEC提出比特币现货ETF申请!根据官方推特数SHIB领头Meme币价格齐飞CloudRushSHIB起飞了!这是近两天圈子里讨论最高的话题。10月27日,比特币迎来登高之后的首次下跌,到达58100美元附近,接近近半月以来的最低水平。自10月20日短暂触及3。7万美元的历美参议员为何反对Facebook推出Novi试点?社交媒体巨头Facebook近期推出了一个试验性的加密货币钱包服务Novi,目前已开始在美国和危地马拉推出其Novi数字钱包的小型试点,并选择Coinbase作为其试点的托管合作伙加密货币总市值突破2。7万亿美元据最新数据显示,目前加密货币总市值已突破2。7万亿美元,24小时涨幅4。4。加密货币总市值突破2。7万亿美元10月16日,加密货币总市值达到2。6万亿美元,仅过去不到一周时间,总市