在Spring Security 5.7.6中有效的配置规则在6.0.1中无效

sxpgvts3  于 2023-03-02  发布在  Spring
关注(0)|答案(2)|浏览(180)
    • 描述错误**在Spring Security 5中有效的配置规则在6.0.1中无效。

在将安全配置迁移到Spring Security 6.0.1之后,如果我们使用了一个错误的凭据,那么浏览器就会被卡住,Hibernate会无休止地运行查询,并且控件不会重定向到登录页面。
为了从Spring Security 5(Spring启动2.1.3.RELEASE)迁移到Spring Security 6.0.1(Spring启动3.0.2),我将SecurityConfiguration.java文件从

@Override
    protected void configure(HttpSecurity http) throws Exception{
        http
                .authorizeRequests()
                .antMatchers("/","/h2-console/**").permitAll()
                .antMatchers("/admin").access("hasAuthority('ADMIN')")
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login").permitAll()
                .and()
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login").permitAll()
                .and()
                .httpBasic();
        http
                .csrf().disable();
        http
                .headers().frameOptions().disable();
    }

    @Override
    protected void  configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsServiceBean())
                .passwordEncoder(passwordEncoder());
    }

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/", "/h2-console/**")
                        .permitAll()
                        .requestMatchers("/admin").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .failureUrl("/login?error=true")
                        .permitAll())
                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/login?logout")
                        .permitAll())
                .httpBasic(Customizer.withDefaults());
        http
                .csrf().disable();
        http
                .headers().frameOptions().disable();
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder())
                .and()
                .build();
    }
}

并将SSUserDetailsService.java文件从

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            User appUser = userRepository.findByUsername(username);

            if(appUser == null){
                System.out.println("User not found with the provided username" + appUser.toString());
                return null;
            }
            System.out.println("User from username " + appUser.toString());
            return new org.springframework.security.core.userdetails.User(
                    appUser.getUsername(),
                    appUser.getPassword(),
                    getAuthorities(appUser));

        } catch (Exception e){
            throw new UsernameNotFoundException("User not found");
        }
    }

    private Set<GrantedAuthority> getAuthorities(User appUser) {
        Set<GrantedAuthority> authorities = new HashSet<>();
        for(Role role: appUser.getRoles()){
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRole());
            authorities.add(grantedAuthority);
        }
        System.out.println("User authorities are" + authorities.toString());
        return authorities;
    }

@Override
    public UserDetails loadUserByUsername(String username){
        try {
            User appUser = userRepository.findByUsername(username);

            if (appUser == null) {
                System.out.println("User not found with the provided username" + appUser.toString());
                return null;
            }
            System.out.println("User from username " + appUser.getUsername());
            return org.springframework.security.core.userdetails.User
                    .withUsername(appUser.getUsername())
                    .password(appUser.getPassword())
                    .roles(getAuthorities(appUser))
                    .build();

        } catch (Exception e) {
            throw new UsernameNotFoundException("User not found");
        }
    }

    private String[] getAuthorities(User appUser) {
        var authorities = new HashSet<String>();
        for (var role : appUser.getRoles()) {
            var grantedAuthority = new SimpleGrantedAuthority(role.getRole());
            authorities.add(grantedAuthority.getAuthority());
        }
        System.out.println("User authorities are " + authorities);
        return Arrays.copyOf(authorities.toArray(),authorities.size(), String[].class);
    }
    • 重现**重现错误行为的步骤(springboot_3.0分支包含Spring Security 6.0.1(Spring Boot 3.0.2)的示例):
  1. git克隆-b springboot_3.0 https://github.com/mhussainshah1/SpringBoot_404.git
    1.光盘Spring启动_404
  2. mvn清洁包
  3. mvnSpring罩:运行
    1.打开带有链接http://localhost:8080/的浏览器
    1.使用用户/密码登录(用户为admin,密码为password)
Hibernate: select u1_0.id,u1_0.email,u1_0.enabled,u1_0.first_name,u1_0.last_name,u1_0.password,u1_0.username from user_data u1_0 where u1_0.username=?
Hibernate: select r1_0.user_id,r1_1.id,r1_1.role from user_data_roles r1_0 join role r1_1 on r1_1.id=r1_0.role_id where r1_0.user_id=?
User from username admin
User authorities are [ADMIN]

1.使用错误凭据登录(用户为dave,密码为begreat)
浏览器卡顿,Hibernate查询无休止运行,直到CTRL + C命令停止

Hibernate: select u1_0.id,u1_0.email,u1_0.enabled,u1_0.first_name,u1_0.last_name,u1_0.password,u1_0.username from user_data u1_0 where u1_0.username=?
Hibernate: select u1_0.id,u1_0.email,u1_0.enabled,u1_0.first_name,u1_0.last_name,u1_0.password,u1_0.username from user_data u1_0 where u1_0.username=?
Hibernate: select u1_0.id,u1_0.email,u1_0.enabled,u1_0.first_name,u1_0.last_name,u1_0.password,u1_0.username from user_data u1_0 where u1_0.username=?
Hibernate: select u1_0.id,u1_0.email,u1_0.enabled,u1_0.first_name,u1_0.last_name,u1_0.password,u1_0.username from user_data u1_0 where u1_0.username=?
    • 预期行为**输入错误凭据(用户名或密码)后,登录页面应显示以下文本Invalid username or password

重现成功行为的步骤(主分支包含Spring Security 5(Spring Boot 2.1.3.RELEASE)的示例):

  1. git克隆https://github.com/mhussainshah1/SpringBoot_404.git
    1.光盘Spring启动_404
  2. mvn清洁包
  3. mvnSpring罩:运行
    1.打开带有链接http://localhost:8080/的浏览器
    1.登录页面将成功打开
    1.使用用户名/密码登录(用户名为admin,密码为password)主页将在链接http://localhost:8080/处打开
    1.使用错误凭据登录(用户为dave,密码为begreat),应重定向到登录页面,然后
    比如Invalid username or password

9udxz4iz

9udxz4iz1#

我的怀疑是,你陷入了一些AuthorizationFilter转发循环。

**要排除此问题,*您可以检查用(已弃用但仍可用).authorizeRequests()替换.authorizeHttpRequests()是否会显示相同的问题(它在幕后使用FilterSecurityInterceptor而不是AuthorizationFilter)。如果是这样-您仍可以尝试我的提示,在下面添加“/login”,但如果使用其他方法时问题消失,您可以如下所示替换弃用项:

装回并在.authorizeHttpRequests()后添加(链)以下之一:

.authorizeHttpRequests()
                .shouldFilterAllDispatcherTypes(false)

.authorizeHttpRequests()
                .dispatcherTypeMatchers(DispatcherType.ASYNC, DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()

这是一个有点弱...
我可能还会尝试**添加"/login*"**作为允许所有调用的第一个requestMatchers中的第三个字符串,因为在转发时尝试对login?...进行身份验证可能(!)是进入循环的原因。
我在迁移Spring安全性时也遇到过类似的问题,而向autorization过滤器发送指令为我完成了这项工作--所以我认为无论如何,有它们也没有坏处。
您可以在此处cf:Spring security 6.0 AuthorizationFilter - questionable default for shouldFilterAllDispatcherTypes

avkwfej4

avkwfej42#

我已将SecurityConfiguration.java文件中的以下方法从

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder())
                .and()
                .build();
}

@Bean
public AuthenticationProvider userDetailsService(BCryptPasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(new SSUserDetailsService(appUserRepository));
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
}

使用DaoAuthenticationProvider可以在身份验证失败时停止循环,并正常工作

相关问题