title: Implementing JWT Authentication on Spring Boot APIs
body: <p>I have a SpringBoot 2.0.2.RELEASE web application, with this config file:</p>
  
  <pre><code>@Override
  protected void configure(HttpSecurity http) throws Exception {
  
      final List&lt;String&gt; activeProfiles = Arrays.asList(env.getActiveProfiles());
      if (activeProfiles.contains("dev")) {
          http.csrf().disable();
          http.headers().frameOptions().disable();
      }
  
      http
          .authorizeRequests()
          .antMatchers(publicMatchers()).permitAll()
          .anyRequest().authenticated()
          .and()
          .formLogin().loginPage("/login").defaultSuccessUrl("/bonanza/list")
          .failureUrl("/login?error").permitAll()
          .and()
          .logout().permitAll();
      }
  </code></pre>
  
  <p>I want to add a Custom JWT based security filter ONLY for the Rest Controllers that will be under the match <code>/rest/**</code> , so I modified the config to this file, but now I can't log into the app, because I have a <strong>HTTP Status 401 – Unauthorized</strong></p>
  
  <pre><code>@Override
  protected void configure(HttpSecurity http) throws Exception {
  
      final List&lt;String&gt; activeProfiles = Arrays.asList(env.getActiveProfiles());
      if (activeProfiles.contains("dev")) {
          http.csrf().disable();
          http.headers().frameOptions().disable();
      }
  
      http
         .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
         // don't create session
         .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
         .authorizeRequests()
         .antMatchers(publicMatchers()).permitAll()
         .anyRequest().authenticated()
         .and()
         .formLogin().loginPage("/login").defaultSuccessUrl("/bonanza/list")
                     .failureUrl("/login?error").permitAll()
         .and()
         .logout().permitAll();
  
  
         // Custom JWT based security filter
         JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
         http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
      }
  </code></pre>
  
  <p>and the filter (that extends from <code>OncePerRequestFilter</code> )</p>
  
  <pre><code>@Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
  
      logger.info("processing authentication for '{}'", request.getRequestURL());
  
  
      if (request.getRequestURI().indexOf("/rest/")==-1) {
          chain.doFilter(request, response);
          return;
      }
  
  
      final String requestHeader = request.getHeader(this.tokenHeader);
  
      String username = null;
      String authToken = null;
      if (requestHeader != null &amp;&amp; requestHeader.startsWith("Bearer ")) {
          authToken = requestHeader.substring(7);
          try {
              username = jwtTokenUtil.getUsernameFromToken(authToken);
          } catch (IllegalArgumentException e) {
              logger.info("an error occured during getting username from token", e);
          } catch (ExpiredJwtException e) {
              logger.info("the token is expired and not valid anymore", e);
          }
      } else {
          logger.info("couldn't find bearer string, will ignore the header");
      }
  
      logger.info("checking authentication for user '{}'", username);
      if (username != null &amp;&amp; SecurityContextHolder.getContext().getAuthentication() == null) {
         logger.info("security context was null, so authorizating user");
  
          // It is not compelling necessary to load the use details from the database. You could also store the information
          // in the token and read it from it. It's up to you ;)
          UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
  
          // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
          // the database compellingly. Again it's up to you ;)
          if (jwtTokenUtil.validateToken(authToken, userDetails)) {
              UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
              authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
              logger.info("authorizated user '{}', setting security context", username);
              SecurityContextHolder.getContext().setAuthentication(authentication);
              }
          }
  
          chain.doFilter(request, response);
      }
  
  ....
  
  @Override
  protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
      return request.getRequestURI().indexOf("/rest/")==-1;
  }
  </code></pre>
  
  <p>in the logger I see</p>
  
  <pre><code>("couldn't find bearer string, will ignore the header"
  </code></pre>
  
  <p>Because I only want to apply the JWT filter in the RestContollers not in all of them, like LoginController</p>
  
  <p>With this config class I can access to the /rest/ URL only being logged in the app.</p>
  
  <pre><code>@Profile("web")
  @Configuration
  @EnableWebSecurity
  public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  
      private static final Logger LOG = LoggerFactory.getLogger(WebSecurityConfig.class);
  
      @Autowired
      private UserSecurityService userSecurityService;
  
  
      @Value("${server.servlet.context-path}")
      private String serverContextPath;
  
      /** The encryption SALT. */
      private static final String SALT = "fd&amp;lkj§sfs23#$1*(_)nof";
  
      @Bean
      public BCryptPasswordEncoder passwordEncoder() {
          return new BCryptPasswordEncoder(12, new SecureRandom(SALT.getBytes()));
      }
  
      @Bean
      @Override
      public AuthenticationManager authenticationManagerBean() throws Exception {
          return super.authenticationManagerBean();
      }
  
      @Configuration
      @Order(1)
      public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
  
          @Autowired
          private JwtAuthenticationEntryPoint unauthorizedHandler;
  
          @Autowired
          private JwtTokenUtil jwtTokenUtil;
  
          @Value("${jwt.header}")
          private String tokenHeader;
  
          @Value("${jwt.route.authentication.path}")
          private String authenticationPath;
  
          @Override
          protected void configure(HttpSecurity http) throws Exception {
  
              http
                      // we don't need CSRF because our token is invulnerable
                      .csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
  
                      // don't create session
                      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                      .authorizeRequests()
                      .antMatchers(“/rest/**”).permitAll().anyRequest().authenticated()
                      .antMatchers(“**/rest/**”).permitAll().anyRequest().authenticated();
  
              // Custom JWT based security filter
              JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
              http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  
              // disable page caching
              http.headers().frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
                      .cacheControl();
  
          }
  
      }
  
      @Configuration
      @Order(0)
      public static class OtherSecurityConfiguration extends WebSecurityConfigurerAdapter {
  
          @Value("${server.servlet.context-path}")
          private String serverContextPath;
  
          @Autowired
          private Environment env;
  
          @Override
          protected void configure(HttpSecurity http) throws Exception {
  
              final List&lt;String&gt; activeProfiles = Arrays.asList(env.getActiveProfiles());
              if (activeProfiles.contains("dev")) {
                  http.csrf().disable();
                  http.headers().frameOptions().disable();
              }
  
              http.authorizeRequests()
                  .antMatchers(publicMatchers())
                  .permitAll()
                  .anyRequest()
                  .authenticated()
                  .and()
                  .formLogin().loginPage("/login").defaultSuccessUrl("/bonanza/list")
                  .failureUrl("/login?error").permitAll()
                  .and()
                  .logout()
                  .permitAll();
          }
  
           private String[] publicMatchers() {
  
               /** Public URLs. */
              final String[] PUBLIC_MATCHERS = {
                      "/webjars/**",
                      serverContextPath + "/css/**",
                      serverContextPath + "/js/**",
                      serverContextPath + "/fonts/**",
                      serverContextPath + "/images/**",                
                      serverContextPath ,
                      "/",
                      "/error/**/*",
                      "/console/**",
                      ForgotMyPasswordController.FORGOT_PASSWORD_URL_MAPPING,
                      ForgotMyPasswordController.CHANGE_PASSWORD_PATH,
                      SignupController.SIGNUP_URL_MAPPING
              };
  
              return PUBLIC_MATCHERS;
  
          }
  
      }
  
      @Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
      }
  
  }
  </code></pre>