Spring Security(学习笔记) --AuthenticationProvider案例演示梳理!

发布于:2024-04-28 ⋅ 阅读:(32) ⋅ 点赞:(0)

重点标识

AuthenticationManager 默认是有parent的。
Security 向Spring容器注册了一个AuthenticationManager ,是一个全局的,也就是所谓的parent。

注册一个AuthenticationManager ,就是一个全局的
如果想要局部的,可以设置一个过滤器链。

我们平时配置的,都是局部的。

正常来说,请求到达的时候,都是局部的AuthenticationManager 来处理的,没配置,系统会自动配置。

如果这个AuthenticationManager 处理不了当前的认证,就会交给parent来处理。也就是所谓的全局的AuthenticationManager 。

代码演示

我们来看一下,自己配置两个局部过滤器链,然后分别登录,能不能达到我们想要的效果。


@Configuration
public class SecurityConfig {

    /**
     *
     * 全局的AuthenticationManager 的用户就是这样配
     * @return
     */
    @Bean
    UserDetailsService globalUser(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("global").password("{noop}123").build());

        return inMemoryUserDetailsManager;
    }

    UserDetailsService adminUser(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("admin").password("{noop}123").build());

        return inMemoryUserDetailsManager;
    }
    UserDetailsService rootUser(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").build());

        return inMemoryUserDetailsManager;
    }

    @Bean
    @Order(2)
    SecurityFilterChain securityRootFilterChain(HttpSecurity http) throws Exception {

        http
                //这里就是我们一开始说的,Security可以提供多个过滤器链,可以在这里进行分流,针对不同的情况,走不同的过滤器
                .securityMatcher("/root/**")
                .authorizeHttpRequests(a->a.anyRequest().authenticated())
                //这里注意,由于过滤器链中存在AuthenticationManager 因此可以直接将数据源加载到过滤器链中
                .userDetailsService(rootUser())
                .formLogin(f ->f.loginProcessingUrl("/root/login").successHandler(((request, response, authentication) -> {
                    response.getWriter().write(authentication.getName());
                })

                ).permitAll())
                .csrf(c -> c.disable());

        return http.build();

    }

    @Bean
    @Order(1)
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
                .securityMatcher("/admin/**")
                .authorizeHttpRequests(a->a.anyRequest().authenticated())
                .userDetailsService(adminUser())
                .formLogin(f ->f.loginProcessingUrl("/admin/login").successHandler(((request, response, authentication) -> {
                    response.getWriter().write(authentication.getName());
                })).permitAll())
                //

                .csrf(c -> c.disable());

        return http.build();

    }
}

使用postMan测一下,全局的,没问题
在这里插入图片描述
root地址下的,也没问题。
在这里插入图片描述
但是,如果我们在root地址下,使用admin登录,那就有问题了。
在这里插入图片描述
但是,如果我们在admin地址下,登录admin,自然是没问题的
在这里插入图片描述
这就达成了我们想要的结果,两个局部的过滤器链,彻底分开,各自走各自的,同时,全局的过滤器链,两个都可以访问。

也验证了我们上一篇所了解的,AutehnticationManager中,如果找不到,就会去寻找它的父类里面的Provider了。

同时,我们可以看一下这部分源码,过滤器链都是通过HttpSecurity构建的,那我们可以想一下,HttpSecurity肯定不会是一个单例,不然写在多都会被覆盖,都是同一个。

进入到HttpSecurityConfiguration中,看一下,果然如此,prototype,可以有多个httpSecurity。在这里插入图片描述
OK,我们再看一下这部分源码
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
建造者模式,构建了一个authenticationBuilder ,然后我们接着往下看。authenticationBuilder.parentAuthenticationManager(this.authenticationManager());
设置parent,这个parent就是 从Spring容器中拿的。

private AuthenticationManager authenticationManager() throws Exception {
        return this.authenticationConfiguration.getAuthenticationManager();
    }
 public AuthenticationManager getAuthenticationManager() throws Exception {
        if (this.authenticationManagerInitialized) {
            return this.authenticationManager;
        } else {
            AuthenticationManagerBuilder authBuilder = (AuthenticationManagerBuilder)this.applicationContext.getBean(AuthenticationManagerBuilder.class);
            if (this.buildingAuthenticationManager.getAndSet(true)) {
                return new AuthenticationManagerDelegator(authBuilder);
            } else {
                Iterator var2 = this.globalAuthConfigurers.iterator();

                while(var2.hasNext()) {
                    GlobalAuthenticationConfigurerAdapter config = (GlobalAuthenticationConfigurerAdapter)var2.next();
                    authBuilder.apply(config);
                }

                this.authenticationManager = (AuthenticationManager)authBuilder.build();
                if (this.authenticationManager == null) {
                    this.authenticationManager = this.getAuthenticationManagerBean();
                }

                this.authenticationManagerInitialized = true;
                return this.authenticationManager;
            }
        }
    }

那它怎么保证Spring容器中一定存在AuthenticationManager呢,就是上面这部分,就是下面这部分代码,Bean,往Spring容器中注册。

@Bean
    public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
        LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
        AuthenticationEventPublisher authenticationEventPublisher = this.getAuthenticationEventPublisher(context);
        DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
        if (authenticationEventPublisher != null) {
            result.authenticationEventPublisher(authenticationEventPublisher);
        }

        return result;
    }

上面这部分是基于Security提供的provider来实现的,我们来看自己配置的。

区别不是很大,有兴趣的,可以对照上面的进行测一下,效果是一样的。


@Configuration
public class SecurityConfig {

    /**
     *
     * 全局的AuthenticationManager 的用户就是这样配
     * @return
     */

    UserDetailsService globalUser(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("global").password("{noop}123").build());

        return inMemoryUserDetailsManager;
    }

    UserDetailsService adminUser(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("admin").password("{noop}123").build());

        return inMemoryUserDetailsManager;
    }

    AuthenticationManager adminAuthenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();

        daoAuthenticationProvider.setUserDetailsService(adminUser());

        ProviderManager providerManager = new ProviderManager(Arrays.asList(daoAuthenticationProvider),globalAuthenticationManager());

        return providerManager;
    }

    private AuthenticationManager globalAuthenticationManager() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();

        daoAuthenticationProvider.setUserDetailsService(globalUser());
        ProviderManager providerManager = new ProviderManager(Arrays.asList(daoAuthenticationProvider));
        return providerManager;

    }

    AuthenticationManager rootAuthenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();

        daoAuthenticationProvider.setUserDetailsService(rootUser());

        ProviderManager providerManager = new ProviderManager(Arrays.asList(daoAuthenticationProvider),globalAuthenticationManager());

        return providerManager;
    }

    UserDetailsService rootUser(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").build());

        return inMemoryUserDetailsManager;
    }

    @Bean
    @Order(2)
    SecurityFilterChain securityRootFilterChain(HttpSecurity http) throws Exception {

        http
                //这里就是我们一开始说的,Security可以提供多个过滤器链,可以在这里进行分流,针对不同的情况,走不同的过滤器
                .securityMatcher("/root/**")
                .authorizeHttpRequests(a->a.anyRequest().authenticated())
                //这里注意,由于过滤器链中存在AuthenticationManager 因此可以直接将数据源加载到过滤器链中
               // .userDetailsService(rootUser())
                .authenticationManager(rootAuthenticationManager())
                .formLogin(f ->f.loginProcessingUrl("/root/login").successHandler(((request, response, authentication) -> {
                    response.getWriter().write(authentication.getName());
                })

                ).permitAll())
                .csrf(c -> c.disable());

        return http.build();

    }

    @Bean
    @Order(1)
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
                .securityMatcher("/admin/**")
                .authorizeHttpRequests(a->a.anyRequest().authenticated())
                .authenticationManager(adminAuthenticationManager())
               // .userDetailsService(adminUser())
                .formLogin(f ->f.loginProcessingUrl("/admin/login").successHandler(((request, response, authentication) -> {
                    response.getWriter().write(authentication.getName());
                })).permitAll())
                //

                .csrf(c -> c.disable());

        return http.build();

    }
}

结语

枯燥,但是有用!


网站公告

今日签到

点亮在社区的每一天
去签到