0

I'm making a spring boot webserver which has spring security and jwt for user authentication/authorization via username and password. But seems like spring recognize /api/users/signup and /api/users/signin as must-be-authenticated URL.

UserController.java:

    @PostMapping("/signin")
    public ResponseEntity<String> login(@ApiParam("Username") @RequestParam String username, //
                                        @ApiParam("Password") @RequestParam String password) {
        return ResponseEntity.ok(userService.signin(username, password));
    }

    @PostMapping("/signup")
    public void signUp(@ApiParam("SignUp User") @RequestBody SignUpRequest request) {
        User user = User.of(request.getUsername(), bCryptPasswordEncoder.encode(request.getPassword()), request.getEmail());
        Role userRole = roleRepository.findByName(RoleName.ROLE_MEMBER).orElse(null);
        user.setRoles(Collections.singleton(userRole));

        userRepository.save(user);
    }

WebSecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = true
)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;


    public WebSecurityConfig(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Disable CSRF (cross site request forgery)
        http.csrf().disable();

        // No session will be created or used by spring security
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // Entry points
        http.authorizeRequests()//
                .antMatchers("/api/users/signin").permitAll()//
                .antMatchers("/api/users/signup").permitAll()//
                .antMatchers("/api/test/**").permitAll()
                .antMatchers("/h2-console/**/**").permitAll()
                // Disallow everything else..
                .anyRequest().authenticated();

        // If a user try to access a resource without having enough permissions
        http.exceptionHandling().accessDeniedPage("/login");

        // Apply JWT
        http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider));

        http.cors().disable();

        // Optional, if you want to test the API from a browser
        // http.httpBasic();

        super.configure(http);
    }

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

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

JwtTokenFilter.java:

public class JwtTokenFilter extends OncePerRequestFilter {

    private JwtTokenProvider jwtTokenProvider;

    public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = jwtTokenProvider.resolveToken(httpServletRequest);
        try {
            if (token != null && jwtTokenProvider.validateToken(token)) {
                Authentication auth = jwtTokenProvider.getAuthentication(token);
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        } catch (CustomException ex) {
            //this is very important, since it guarantees the user is not authenticated at all
            SecurityContextHolder.clearContext();
            httpServletResponse.sendError(ex.getHttpStatus().value(), ex.getMessage());
            return;
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

MyUserDetailsService.java:

@Service
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        final User user = userRepository.findByUsername(username).orElseThrow(() -> new CustomException("User doesn't exist", HttpStatus.NOT_FOUND));

        List<GrantedAuthority> authorities = user.getRoles().stream().map(role ->
            new SimpleGrantedAuthority(role.getName().getAuthority())
        ).collect(Collectors.toList());

        if (user == null) {
            throw new UsernameNotFoundException("User '" + username + "' not found");
        }

        return org.springframework.security.core.userdetails.User//
                .withUsername(username)
                .password(user.getPassword())
                .authorities(authorities)
                .accountExpired(false)
                .accountLocked(false)
                .credentialsExpired(false)
                .disabled(false)
                .build();
    }
}

When I request to both of these links as I told above. It's done quickly by giving me 401 HTTP error code while testing on postman.

Both this link and this link are not helpful at all.

Higanbana
  • 489
  • 10
  • 26

1 Answers1

0

You might want to try excluding these URLs from the WebSecurity section, instead, so that they do not get processed by Spring Security and your JwtTokenFilter at all.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  public void configure(final WebSecurity web) throws Exception {
      web.ignoring()
       .antMatchers("/api/users/signin").antMatchers("/api/users/signup");
  }
}
Marco Behler
  • 3,627
  • 2
  • 17
  • 19