7

I'm using Spring security to secure some endpoints in my REST service.

here's the security configuration class:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // Other methods

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                .csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/",
                        "/favicon.ico",
                        "/**/*.png",
                        "/**/*.gif",
                        "/**/*.svg",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js")
                .permitAll()
                .antMatchers(HttpMethod.POST, "/api/auth/**")
                .permitAll()
                .anyRequest()
                .authenticated();

        // Add our custom JWT security filter
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }
}

As you can see i'm given the full access to /api/auth/signup and /api/auth/signin by using: .antMatchers(HttpMethod.POST, "/api/auth/**").permitAll()

for some reason when i tried those request in the postman, the "signup" request worked fine, but "signin" didn't works and gives me "401 Unauthorized"
i tried also .antMatchers("/**").permitAll()

here's my controller:

@RestController
public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/api/auth/signup")
    public ResponseEntity<RestResponse> registerUser(@Valid @RequestBody SignUpRequest signUpRequest,
                                                     UriComponentsBuilder uriComponentsBuilder)  {
        RestResponse restResponse = this.userService.register(signUpRequest);
        UriComponents uriComponents = uriComponentsBuilder.path("/users").buildAndExpand();
        return ResponseEntity.created(uriComponents.toUri()).body(restResponse);
    }

    @PostMapping("/api/auth/signin")
    public ResponseEntity<JwtAuthenticationResponse> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
        return ResponseEntity.ok(this.userService.login(loginRequest));
    }
}
Ayoub k
  • 7,788
  • 9
  • 34
  • 57
  • Can you try disabling the line `.authenticationEntryPoint(this.jwtAuthenticationEntryPoint)` ? Usually entry point methods also return 401 status. – Kedar Joshi Oct 20 '18 at 12:35
  • it gives me 403 forbidden – Ayoub k Oct 22 '18 at 21:14
  • Show your implementation of `JwtAuthenticationFilter`. – dur Oct 23 '18 at 08:21
  • @Ayoubk Your config looks ok - can you try with removing the HttpMethod.POST and make it antMatchers("/api/auth/**") just for testing purposes. Can you confirm you're sending the correct HTTP request types from Postman when invoking the REST endpoints? – hovanessyan Oct 23 '18 at 16:10
  • Please add the `signin` request to the question. – Boris Oct 26 '18 at 16:42

7 Answers7

7

I had the same issue, not sure, but I think you need this order:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/api/auth/**")
            .permitAll()
            .antMatchers("/",
                    "/favicon.ico",
                    "/**/*.png",
                    "/**/*.gif",
                    "/**/*.svg",
                    "/**/*.jpg",
                    "/**/*.html",
                    "/**/*.css",
                    "/**/*.js")
            .permitAll()                   
            .anyRequest()
            .authenticated()
            .and()
            .cors()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .csrf()
            .disable();

    // Add our custom JWT security filter
    http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

}
Kirill Shiryaev
  • 339
  • 2
  • 8
1

Your configuration is not working due to order in which the antMatcher is evaluated

 .and()
 .authorizeRequests()
 .antMatchers("/",
    "/favicon.ico",
    "/**/*.png",
    "/**/*.gif",
    "/**/*.svg",
    "/**/*.jpg",
    "/**/*.html",
    "/**/*.css",
    "/**/*.js")
 .permitAll()
 .antMatchers(HttpMethod.POST, "/api/auth/**")
 .permitAll()
 .anyRequest()
 .authenticated();

The order of the request match rule matters and more specific rules should go first. There is some conflict between both antMatcher rules and therefore the second rule i.e .antMatchers(HttpMethod.POST, "/api/auth/")** is ignored.

Therefore the order should be following :-

 .antMatchers(HttpMethod.POST, "/api/auth/**")
 .permitAll()
 .antMatchers("/",
    "/favicon.ico",
    "/**/*.png",
    "/**/*.gif",
    "/**/*.svg",
    "/**/*.jpg",
    "/**/*.html",
    "/**/*.css",
    "/**/*.js")
 .permitAll()
karans123
  • 796
  • 6
  • 8
0

The last time i did it i remember the order is important.

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                .csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/auth/**")
                .permitAll()
                .antMatchers("/",
                        "/favicon.ico",
                        "/**/*.png",
                        "/**/*.gif",
                        "/**/*.svg",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js")
                .permitAll()                   
                .anyRequest()
                .authenticated();

        // Add our custom JWT security filter
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }
shadow
  • 800
  • 2
  • 16
  • 33
0

You need to add the following to your configure method /error is the default fall back when error occurs to the application due to any exception and it is secured by default.

    protected void configure(HttpSecurity httpSecurity) throws Exception {
    //disable CRSF
    httpSecurity
            //no authentication needed for these context paths
            .authorizeRequests()
            .antMatchers("/error").permitAll()
            .antMatchers("/error/**").permitAll()
            .antMatchers("/your Urls that dosen't need security/**").permitAll()

Also the below code snippet

 @Override
 public void configure(WebSecurity webSecurity) throws Exception
   {
     webSecurity
    .ignoring()
        // All of Spring Security will ignore the requests
        .antMatchers("/error/**")
     }  

Now you will not get 401 and get 500 exception with details when an exception occurred for permitAll Urls

Guru Cse
  • 2,805
  • 2
  • 18
  • 15
0

I was having the same problem and it was due to the fact that I wasn´t using the default jdbc schema, so I was passing the queries needed by the default UserDetailsService, and my authorities table was empty, so it was not getting results searching by the username.

0

I had the same error but my fault is to try login with the email and pass credentials. But my userdetailsservice loaduser by name not by email. After changing like below it worked successfully

public class UserDetailsImplServices implements UserDetailsService {

    @Autowired
    private UserRepository UserRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        //edit here to findbyemail not byname 
        UserModel user=UserRepository.findByEmail(email).get();
        return UserDetailsImpl.build(user);
    }
    
} 
0

.anyRequest().authenticated() is preventing the permit all configuration. I tried to replace the .anyRequest() with antMatchers("/<context-path>/**"). It worked for me.

 protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                .and()
                .csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/",
                        "/favicon.ico",
                        "/**/*.png",
                        "/**/*.gif",
                        "/**/*.svg",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js")
                .permitAll()
                .antMatchers(HttpMethod.POST, "/api/auth/**")
                .permitAll()
                .antMatchers("/<context-path>/**")
                .authenticated();

        // Add our custom JWT security filter
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }