Spring Security:授权后发出 403 单授权

Posted

技术标签:

【中文标题】Spring Security:授权后发出 403 单授权【英文标题】:Spring Security: issues 403 after authorization with single granted 【发布时间】:2022-01-06 20:24:56 【问题描述】:

使用 Spring Boot 2 + Spring Security Starter。

授权用户,但由于某种原因给出错误403。

我尝试用不同的方式进行配置,但它不起作用。

授权成功后(loadUserByUsername 方法工作正常)它在所有带有 /admin 前缀的页面上显示 403,而在授权之前,切换到带有此前缀的任何页面都会导致重定向到 /login

@Controller
public class AdminController 
    @RequestMapping(value = "/admin", method = GET, POST)
    public String adminMainPage() 
        return "redirect:/admin/article";
    


@Controller
@RequestMapping("/admin/article")
public class ArticleController 
  @RequestMapping(value = "", method = GET, POST)
  public ModelAndView indexAdminPage(...)
  ...
  


@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements UserDetailsService 
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf().disable()
                .userDetailsService(this)
                .authorizeRequests()
                .antMatchers("/", "/login",
                        "/login*", "/assets/**", "/lib/**", "/page.scripts/*").permitAll()
                .antMatchers("/admin/**").hasAnyRole("ADMIN")
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .usernameParameter("login")
                .passwordParameter("password")
                .successForwardUrl("/admin")
                .permitAll()
                .and()
                .logout()
                .deleteCookies("JSESSIONID")
                .permitAll();
    

    private Collection<? extends GrantedAuthority> adminGrantedAuthoritySet = new HashSet<>() 
        add(new SimpleGrantedAuthority("ADMIN"));
    ;

    private final UserRepository userRepository;

    public WebSecurityConfig(UserRepository userRepository ) 
        this.userRepository = userRepository;
    

    @Override
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException 
        Optional<UserEntity> optionalUser = userRepository.findByLogin(login);
        if (optionalUser.isEmpty()) 
            throw new UsernameNotFoundException("User by login '" + login + "' not found");
         else 
            UserEntity userEntity = optionalUser.get();
            return new User(login, userEntity.getPassword(), adminGrantedAuthoritySet);
        
    

【问题讨论】:

【参考方案1】:

在 Spring Security 中,roleauthority 之间存在区别。role 是一个以 "ROLE_" 为前缀的 authority。在此示例中,权限"ROLE_ADMIN" 与角色"ADMIN" 相同。

您将您的管理员authorities 设置为new SimpleGrantedAuthority("ADMIN") 的列表,但您限制了对.hasAnyRole("ADMIN") 的访问。

您需要更改其中一项配置。 如果您使用.hasAnyRole("ADMIN"),则应将管理员authorities 列表更改为使用new SimpleGrantedAuthority("ROLE_ADMIN")。 否则,如果您希望您的列表为new SimpleGrantedAuthority("ADMIN"),那么您应该使用.hasAnyAuthority("ADMIN")

【讨论】:

【参考方案2】:

首先,我建议您将 UserDetailsS​​ervice 与 WebSecurityConfig 分开。

为 UserDetailsS​​ervice 提供一个单独的类,例如

@Service("customCustomerDetailsService")
public class CustomCustomerDetailsService implements UserDetailsService  

    @Autowired
    private CustomerRepository customers;    




    @Override
    public UserDetails loadUserByUsername(String email)  

      return this.customers.findByEmail(email)
            .orElseThrow(() -> new UsernameNotFoundException("Username: " + email + " not found"));



    


那么你的 UserEntity 应该在你设置权限的地方实现 UserDetails 类。查看答案 //userdetails

@Override
    public Collection<? extends GrantedAuthority> getAuthorities() 
        return this.roles.stream().map(SimpleGrantedAuthority::new).collect(toList());
    


    @Override
    public String getUsername() 

        return this.getEmail();
    


    @Override
    public boolean isAccountNonExpired() 

        return true;
    


    @Override
    public boolean isAccountNonLocked() 
        return true;
    


    @Override
    public boolean isCredentialsNonExpired() 
        return true;
    


    @Override
    public boolean isEnabled() 
        return true;
    

    @Transient
    private List<String> roles = Arrays.asList("ROLE_USER");
    public List<String> getRoles() 
        return roles;
    

然后你需要 DAOauthentication 管理器,它像这样使用 UserDetailsS​​ervice:

@Bean
    public DaoAuthenticationProvider authenticationProvider() 
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(encoder());

        return authProvider;

    
@Bean
    @Override
    public UserDetailsService userDetailsService() 

        return new CustomCustomerDetailsService();

    

我不知道将所有内容都放在 WebSecurityConfig 中是一种好习惯,它会很复杂并且容易出错!

【讨论】:

以上是关于Spring Security:授权后发出 403 单授权的主要内容,如果未能解决你的问题,请参考以下文章

AJAX / Spring MVC - 没有 Spring Security 的 403 禁止错误

Spring security UsernamePasswordAuthenticationToken 始终返回 403:用户凭据已过期

登录后 Spring Security 总是返回 403 accessDeniedPage [重复]

Spring Security + Angular App REST Token Based Authentication = 403 Forbidden on POST

在 Grails 3 应用程序中使用 Spring Security Rest 对“refresh_token”请求的 403 响应

在 Spring Security 中使用 CSRF 令牌获取 403