sandro11111
sandro11111

Reputation: 1

Spring Security: issues 403 after authorization with single granted

Used Spring Boot 2 + Spring Security Starter.

Authorizes users, but for some reason gives an error 403.

I tried to configure in different ways, but it does not work.

After successful authorization (the loadUserByUsername method works fine) it shows 403 on all pages with the / admin prefix, and before authorization, switching to any page with this prefix leads to a redirect to / 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);
        }
    }
}

Upvotes: 0

Views: 290

Answers (2)

tksilicon
tksilicon

Reputation: 4446

First, I will advice that you separate UserDetailsService from the WebSecurityConfig.

Have a separate class for UserDetailsService like

@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"));



    }

}

Then your UserEntity should implement UserDetails class where you set the authorities.See the answer //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;
    }

Then you need DAOauthentication manager which makes use of the UserDetailsService like this:

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

        return authProvider;

    }
@Bean
    @Override
    public UserDetailsService userDetailsService() {

        return new CustomCustomerDetailsService();

    }

I don't know think putting everything in the WebSecurityConfig is good practice and it will be complicated and prone to errors!

Upvotes: 0

In Spring Security there is a distinction between a role and an authority.
A role is an authority that is prefixed with "ROLE_". In this example the authority "ROLE_ADMIN" is the same as the role "ADMIN".

You are setting your admin authorities to be a list of new SimpleGrantedAuthority("ADMIN"), but you are restricting access to .hasAnyRole("ADMIN").

You need to change one of those configurations.
If you use .hasAnyRole("ADMIN"), then you should change the admin authorities list to use new SimpleGrantedAuthority("ROLE_ADMIN").
Otherwise, if you want your list to be new SimpleGrantedAuthority("ADMIN"), then you should use .hasAnyAuthority("ADMIN").

Upvotes: 1

Related Questions