Spring Security using HTTP headers

27,323

Solution 1

the minimal code addition is to define a filter and add it to the security configuration, smth like

XHeaderAuthenticationFilter.java

@Component
public class XHeaderAuthenticationFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    String xAuth = request.getHeader("X-Authorization");

    User user = findByToken(xAuth);

    if (user == null) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token invalid");
    } else {
        final UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);

        filterChain.doFilter(request, response);
    }
}

//need to implement db user validation...
private User findByToken(String token) {
    if (!token.equals("1234"))
        return null;

    final User user = new User(
            "username",
            "password",
            true,
            true,
            true,
            true,
            Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));

    return user;
}
}

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .csrf().disable()
            .authorizeRequests().anyRequest().authenticated()
            .and()
            .exceptionHandling()
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
            .and()
            .addFilterBefore(new XHeaderAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

another approach is to use spring's AOP to define annotation of some logic to perform before entering the annotated controller method

Solution 2

You should avoid using default org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter since it gets client supplied username and password from the parameters of your request and you really need to get them from the headers.

So, you should write a custom AuthenticationFilter extending referred UsernamePasswordAuthenticationFilter to change its behaviour to fit your requirements:

public class HeaderUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

  public HeaderUsernamePasswordAuthenticationFilter() {
    super();
    this.setFilterProcessesUrl("/**");
    this.setPostOnly(false);
  }

  /* (non-Javadoc)
   * @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#obtainPassword(javax.servlet.http.HttpServletRequest)
   */
  @Override
  protected String obtainPassword(HttpServletRequest request) {
    return request.getHeader(this.getPasswordParameter());
  }

  /* (non-Javadoc)
   * @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#obtainUsername(javax.servlet.http.HttpServletRequest)
   */
  @Override
  protected String obtainUsername(HttpServletRequest request) {
    return request.getHeader(this.getPasswordParameter());
  }

}

This filter example extends org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter listens to every request and gets username and password from headers instead of parameters.

Then you should change the configuration this way, setting your filter in the UsernamePasswordAuthenticationFilter position:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterAt(
                new HeaderUsernamePasswordAuthenticationFilter(), 
                UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
            .antMatchers("/index.html").permitAll()
            .antMatchers("/swagger-ui.html").hasRole("ADMIN")
            .anyRequest().authenticated();

}

Solution 3

In memory authentication would serve your purpose

@Configuration
@EnableWebMvc
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
        .withUser("user1").password("password1").roles("USER")
        .and()
        .withUser("user2").password("password2").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().fullyAuthenticated();
        http.httpBasic();   
    }

}
Share:
27,323

Related videos on Youtube

Chayma Atallah
Author by

Chayma Atallah

Updated on July 21, 2022

Comments

  • Chayma Atallah
    Chayma Atallah almost 2 years

    I am trying to add security to my Spring Boot application. My current application is using REST controllers and every time I get a GET or POST request I read the HTTP header to retrieve the user and password in order to validate them against the properties file I have all my users stored. I want to change this to using Spring Security and this is what I got so far:

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/index.html").permitAll()
                .antMatchers("/swagger-ui.html").hasRole("ADMIN")
                .anyRequest().authenticated();
        }
    
        @Bean
        public UserDetailsService userDetailsService() {
            InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
            manager.createUser(User.withUsername("admin").password("password").roles("ADMIN").build());
        }
    }
    

    How can I tell the configure method that the user credentials are to be retrieved from the header and not a login form?

  • Chayma Atallah
    Chayma Atallah about 7 years
    Thank you for your answer but I have more than one user (I just posted on as an example)
  • gladiator
    gladiator about 7 years
    see this you can create your own AuthenticationManager and add user accounts to it docs.spring.io/spring-boot/docs/current/reference/html/…
  • Chayma Atallah
    Chayma Atallah about 7 years
    OK thanks but how do I read the role now in my properties file now?
  • jlumietu
    jlumietu about 7 years
    Please edit your post including your properties file structure. You must build a UserDetailsService which reads the entries from your file
  • Pino
    Pino almost 4 years
    Interesting example. In my code I check for the existence of a security context at the beginning of the filter to avoid accessing the DB for each request. Of course I also allow session creation.
  • shahaf
    shahaf almost 4 years
    @Pino , yes, that would be more efficient, keep in mind that usually when using headers auth it comes hand in hand with stateless... so basically there isn't any session you can get from the context... but this code is a minimal and can be extended vastly...

Related