一步一步实现若依框架--2.5匿名注解@Anonymous

发布时间 2023-08-04 16:53:26作者: 混迹
 1实现绕过权限认证,可以直接访问某些接口。
这些部分可以直接在Spring Security中的配置去写,也可以像这个主角这样给添加了注解的方法或类进行放行。
  原理:在spring security设置拦截前,获取到所有添加了该注解的请求,把这些请求添加到放开拦截的配置中。
2实现

a)新增注解(注解中内容是可以空的)

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous {
}

b)在spring security进行配置的时候,获取到添加了注解的类或方法

  之前使用注解的时候都是在切面中,也就是当程序执行到添加了注解时程序能运行到切面里的代码。现在要在spring初始化bean后获取到全部的请求信息,在spring的生命周期 afterPropertiesSet时 通过调用
  RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
  Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
可以获取到。这个逻辑封装到了工具类 PermitAllUrlProperties中。
@Configuration
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware {
    /**
     * 正则表达式 匹配path variable
     * 如: @GetMapping(value = "/configKey/{configKey}") 进行匹配后替换为/configKey/*
     *
     */
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");

    /**
     * spring 上下文
     * 从中可以获取到各种bean
     */
    private ApplicationContext applicationContext;
    /**
     * 该集合中保存了全部标记过匿名注解的url请求
     */
    private List<String> urls = new ArrayList<>();

    public String ASTERISK = "*";

    @Override
    public void afterPropertiesSet()  {
        // 获取全部的handlerMappings
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();

        map.keySet().forEach(info->{
            HandlerMethod handlerMethod = map.get(info);
            // 获取方法上的注解 替代path variable 为 *
            Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
            Optional.ofNullable(method).ifPresent(anonymous->info.getPatternsCondition().getPatterns()
                    .forEach(url->urls.add(RegExUtils.replaceAll(url,PATTERN,ASTERISK))));

            // 获取类上的注解
            Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
            Optional.ofNullable(controller).ifPresent(anonymous->info.getPatternsCondition().getPatterns()
                    .forEach(url->urls.add(RegExUtils.replaceAll(url,PATTERN,ASTERISK))));

        });
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public List<String> getUrls() {
        return urls;
    }

    public void setUrls(List<String> urls) {
        this.urls = urls;
    }
}
PermitAllUrlProperties 工具类

c)配置放开拦截

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    @Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    // 注解标记允许匿名访问的url
    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
    permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
    ... 其他配置
 }

  3. 测试

  目前对于没有添加匿名注解的都会拦截不允许访问。

  4.总结

  公共的方法很多都是通过注解、切面进行实现的;结合spring的生命周期函数,实现公共逻辑的处理。很多时候是结合了servlet和过程和spring的能力进行处理的。

  代码中添加了很多spring security的配置,以及涉及到的一些工具类。framework.config.SecurityConfig是Spring Security的配置的核心,从这个类会把对security的扩展配置进去的。UserDetailsServiceImpl是重点类,实现认证过的关键类。spring security的配置和扩展很多,需要理解下原理去梳理下常见的配置内容,但是总之就是去配置和扩展框架。

 

代码地址:https://github.com/hunji/RYMirror/releases/tag/2.5%E5%8C%BF%E5%90%8D%E6%B3%A8%E8%A7%A3--SpringSecurity

UserDetailsServiceImpl