Spring Security Always returning 403 forbidden, Access denied
Asked Answered
G

3

13

I want to enable admin to access admin page and do admin stuff, but when I try to do that by setting that the url with /admin/** can only be accessed by user with role admin, it returns 403 Forbidden, access denied. But the user has authorities set to ROLE_ADMIN I checked. What am I doing wrong?

My Controller for user login

@RestController
public class UserController {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthorityService authorityService;

    @Autowired
    private UserAuthorityService userAuthorityService;

    @Autowired
    TokenUtils tokenUtils;

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/api/login", method = RequestMethod.POST, produces = "text/html")
    public ResponseEntity<String> login(@RequestBody LoginDTO loginDTO) {
        try {
//          System.out.println(loginDTO.getUsername() + " " + loginDTO.getPassword());
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
                    loginDTO.getUsername(), loginDTO.getPassword());

            Authentication authentication = authenticationManager.authenticate(token);

            SecurityContextHolder.getContext().setAuthentication(authentication);

            UserDetails details = userDetailsService.loadUserByUsername(loginDTO.getUsername());

            return new ResponseEntity<String>(tokenUtils.generateToken(details), HttpStatus.OK);
        } catch (Exception ex) {
            return new ResponseEntity<String>("Invalid login", HttpStatus.BAD_REQUEST);
        }
    }

    @RequestMapping(value = "/api/register", method = RequestMethod.POST, produces = "text/html")
    public ResponseEntity<String> register(@RequestBody RegisterDTO registerDTO) {
        try {
            System.out.println(registerDTO);
            User user = userService.findUserByUsername(registerDTO.getUsername());
//            // Check if user with that username exists
            if(user != null){
                // User with that username is found
                return new ResponseEntity<String>("User with that username exists", HttpStatus.BAD_REQUEST);
            }
            // We need to save the user so his ID is generated
            User newUser = userService.saveUser(new User(registerDTO));

            UserAuthority userAuthority = userAuthorityService.save(new UserAuthority(newUser, authorityService.findOneByName("User")));

            Set<UserAuthority> authorities = new HashSet<>();
            authorities.add(userAuthority);

            newUser.setUserAuthorities(authorities);
            User savedUser = userService.save(newUser);
            return new ResponseEntity<String>("You have registered successfully with username " + savedUser.getUsername(), HttpStatus.OK);
        } catch (Exception ex) {
            return new ResponseEntity<String>("Invalid register", HttpStatus.BAD_REQUEST);
        }
    }
}

I can say that I test my app with postman and login and registration are working fine. When the user is logged in I can the token with the correct data and users authorities, but why when I try to access /admin/building/add url it is returning 403 error?

My Controller for adding building for admin page:

@RestController
public class BuildingController {

    @Autowired
    private BuildingService buildingService;

    @RequestMapping(value = "/admin/building/add", method = RequestMethod.POST, produces = "text/html")
    public ResponseEntity<String> addBuilding(@RequestBody BuildingDTO buildingDTO) {
        try{
            Building newBuilding = new Building(buildingDTO);
            return new ResponseEntity<String>(newBuilding.getName(), HttpStatus.OK);
        }catch (Exception ex) {
            return new ResponseEntity<String>("Data was not valid", HttpStatus.BAD_REQUEST);
        }
    }
}

My SecurityConfiguration.java

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureAuthentication(
            AuthenticationManagerBuilder authenticationManagerBuilder)
            throws Exception {

        authenticationManagerBuilder
                .userDetailsService(this.userDetailsService).passwordEncoder(
                        passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public AuthenticationTokenFilter authenticationTokenFilterBean()
            throws Exception {
        AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
        authenticationTokenFilter
                .setAuthenticationManager(authenticationManagerBean());
        return authenticationTokenFilter;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers("/index.html", "/view/**", "/app/**", "/", "/api/login", "/api/register").permitAll()
                // defined Admin only API area 
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest()
                .authenticated()
                .and().csrf().disable();
                //if we use AngularJS on client side
//              .and().csrf().csrfTokenRepository(csrfTokenRepository()); 

        //add filter for adding CSRF token in the request 
        httpSecurity.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);

        // Custom JWT based authentication
        httpSecurity.addFilterBefore(authenticationTokenFilterBean(),
                UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * If we use AngularJS as a client application, it will send CSRF token using 
     * name X-XSRF token. We have to tell Spring to expect this name instead of 
     * X-CSRF-TOKEN (which is default one)
     * @return
     */
    private CsrfTokenRepository csrfTokenRepository() {
          HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
          repository.setHeaderName("X-XSRF-TOKEN");
          return repository;
    }
  }

I should mention that I am using Angularjs for frontend, but even so I can login and the correct authorities are displayed for that user. But for some reason I can not access the admin page, even if I login as admin.

Also I tried .hasAuthority("ROLE_ADMIN") and .hasRole("ROLE_ADMIN")(which displays an error for ROLE_) and so I changed it to .hasRole("ADMIN") but it is still not working.

In the database the role for admin is saved as ROLE_ADMIN.

Glennaglennie answered 10/11, 2017 at 11:22 Comment(2)
You are totally bypassing Spring Security with your login. Use Spring Security to handle to login don't reinvent your own.Hafler
Check here : https://mcmap.net/q/204637/-spring-security-configuration-http-403-error/…Boru
H
11

Try like this :

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private static String REALM="MY_TEST_REALM";

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("ADMIN");
        auth.inMemoryAuthentication().withUser("tom").password("abc123").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

      http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/user/**").hasRole("ADMIN")
        .and().httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint())
        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//We don't need sessions to be created.
    }

    @Bean
    public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
        return new CustomBasicAuthenticationEntryPoint();
    }

    /* To allow Pre-flight [OPTIONS] request from browser */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }
}

For a complet configuration example : Secure Spring REST API using Basic Authentication

Hayden answered 10/11, 2017 at 11:39 Comment(1)
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**"); is not recommended and do not use it.Chukar
P
4
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();// We don't need sessions to be created.
    }

}

This did it for me. Now I am able to submit my post requests successfully

Polymath answered 18/5, 2019 at 19:32 Comment(1)
This disables CSRF, not sessions and not recommended. owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) THe reason your requests were failing is you were not providing the CSRF cookie/headerSibyls
F
0

Try this in SecurityConfig:

.antMatchers("/api/admin").access("hasRole('ADMIN')")
.antMatchers("/api/user").access("hasRole('ADMIN') or hasRole('USER')")
Framing answered 28/2, 2020 at 15:27 Comment(2)
Does this answer provide anything new?Insectivore
Please edit your answer to explain what this does and how it solves the problem described in the questionEntrap

© 2022 - 2024 — McMap. All rights reserved.