Integrate Servlet Security using Basic Authentication

121 Views Asked by At

I need to expose the rest api and we are currently using servlet. No frontend needed to test our api and we are only using postman to test the endpoint. My Get and Delete api mapping are working fine except the basic authentication security. I found a reference in google related to my requirements and it is not working when I select the Basic Auth in postman it didn't call the RestAuthenticationFilter method. This is my code:

public class RestAuthenticationFilter implements Filter {
      public static final String AUTHENTICATION_HEADER = "Authorization";

      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
          throws IOException, ServletException {
          System.out.println("Filter");
        if (request instanceof HttpServletRequest) {
          final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
          final String authCredentials = httpServletRequest.getHeader(AUTHENTICATION_HEADER);
          System.out.println("authCredentials: "+authCredentials);

          final AuthService authService = new AuthService();
          final boolean authenticationStatus = authService.authenticate(authCredentials);
          System.out.println("authenticationStatus: "+authenticationStatus);
          if (authenticationStatus) {
            chain.doFilter(request, response);
          } else {
            if (response instanceof HttpServletResponse) {
              HttpServletResponse httpServletResponse = (HttpServletResponse) response;
              httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            }
          }
        }
      }

      public void init(FilterConfig filterConfig) throws ServletException {
        // No Implementation
      }

      public void destroy() {
        // No Implementation
      }
}

Add service

public class AuthService {
      public boolean authenticate(final String authCredentials) {
         System.out.println("autservice");
        if (null == authCredentials) {
          return false;
        }

        // Header value format for Basic authentication will be "Basic encodedstring"
        final String encodedUserAuthCredentials = authCredentials.replaceFirst("Basic ", "");
        System.out.println("encodedUserAuthCredentials: "+encodedUserAuthCredentials);
        String decodedUserAuthCredentials = null;
        try {
//          sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder();
//          String userpassDecoded = new String(dec.decodeBuffer(encodedUserAuthCredentials));
//          String base64String = BaseEncoding.base64().encode(encodedUserAuthCredentials.getBytes("UTF-8"));
//          byte[] decodedBytes = Base64.getDecoder().decode(encodedUserAuthCredentials);
//          decodedUserAuthCredentials = base64String;
            decodedUserAuthCredentials = DatatypeConverter.printBase64Binary(("admin:admin").getBytes("UTF-8"));
//          System.out.println("base64String: "+base64String);
            System.out.println("decodedUserAuthCredentials: "+decodedUserAuthCredentials);
        } catch (IOException e) {
          System.out.println("LOGGER");
        }

        final StringTokenizer tokenizer = new StringTokenizer(decodedUserAuthCredentials, ":");
        final String userName = tokenizer.nextToken();
        final String userPassword = tokenizer.nextToken();

        return "admin".equals(userName) && "admin".equals(userPassword);
      }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>test</display-name>
  <servlet>
    <servlet-name>
        AppContoller
    </servlet-name>
    <servlet-class>
        com.test.control.web.AppController
    </servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>
        AppController
    </servlet-name>
    <url-pattern>
        /control/*
    </url-pattern>
  </servlet-mapping>
   
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
   <filter>
      <filter-name>AuthenticationFilter</filter-name>
      <filter-class>com.test.control.web.api.RestAuthenticationFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>AuthenticationFilter</filter-name>
      <url-pattern>/control/*</url-pattern>
   </filter-mapping>

</web-app>

May I know what is wrong in my code?

If I select the Bearer Token, API Key under Authorization Type in postman it hits the RestAuthenticationFilter method but not when I select the Basic Auth.

1

There are 1 best solutions below

0
Ahmed Nabil On

You have created a custom filter, you still have to add it to the security filter chain with something like that

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        // ...
        .addFilterBefore(new RestAuthenticationFilter(), AuthorizationFilter.class); 
    return http.build();
}

TIP: Instead of implementing Filter, you can extend from OncePerRequestFilter which is a base class for filters that provides a doFilterInternal method with the HttpServletRequest and HttpServletResponse parameters to avoid casting.

also you can refer to docs to help you understand how basic Auth is handled by default which you can use to provide some implementations instead of creating your own classes