How to secure REST API with Spring Boot and Spring Security?

JavaSpringRestSpring Security

Java Problem Overview


I know that securing REST API is widely commented topic but I'm not able to create a small prototype that meets my criteria (and I need to confirm that these criteria are realistic). There are so many options how to secure resources and how work with Spring security, I need to clarify if my needs are realistic.

My requirements

  • Token based authenticator - users will provide its credentials and get unique and time limited access token. I would like to manage token creation, checking validity, expiration in my own implementation.
  • Some REST resources will be public - no need to authenticate at all,
  • Some resources will be accessible only for users with administrator rights,
  • Other resource will be accessible after authorization for all users.
  • I don't want to use Basic authentication
  • Java code configuration (not XML)

Current status

My REST API works very well, but now I need to secure it. When I was looking for a solution I created a javax.servlet.Filter filter:

  @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
        Account account = accountDao.find(accessToken);

        if (account == null) {    
            throw new UnauthorizedException();    
        }

        chain.doFilter(req, res);

    }

But this solution with javax.servlet.filters doesn't work as I need because there is an issue with exception handling via @ControllerAdvice with Spring servlet dispatcher.

What I need

I would like to know if these criteria are realistic and get any help, how to start securing REST API with Spring Security. I read many tutorials (e.g. [Spring Data REST + Spring Security] 1) but all work in very basic configuration - users with their credentials are stored in memory in configuration and I need to work with DBMS and create own authenticator.

Please give me some ideas how to start.

Java Solutions


Solution 1 - Java

> Token based authentication - users will provide its credentials and get > unique and time limited access token. I would like to manage token > creation, checking validity, expiration in my own implementation.

Actually, use Filter for token Auth - best way in this case

Eventually, you can create CRUD via Spring Data for managing Token's properties like to expire, etc.

Here is my token filter: http://pastebin.com/13WWpLq2

And Token Service Implementation

http://pastebin.com/dUYM555E

> Some REST resources will be public - no need to authenticate at all

It's not a problem, you can manage your resources via Spring security config like this: .antMatchers("/rest/blabla/**").permitAll()

> Some resources will be accessible only for users with administrator rights,

Take a look at @Secured annotation to class. Example:

@Controller
@RequestMapping(value = "/adminservice")
@Secured("ROLE_ADMIN")
public class AdminServiceController {

> The other resource will be accessible after authorization for all users.

Back to Spring Security configure, you can configure your url like this:

    http
            .authorizeRequests()
            .antMatchers("/openforall/**").permitAll()
            .antMatchers("/alsoopen/**").permitAll()
            .anyRequest().authenticated()

> I don't want to use Basic authentication

Yep, via token filter, your users will be authenticated.

> Java code configuration (not XML)

Back to the words above, look at @EnableWebSecurity. Your class will be:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

You have to override the configure method. Code below, just for example, how to configure matchers. It's from another project.

    @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/assets/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .loginPage("/login")
                .defaultSuccessUrl("/", true)
                .successHandler(customAuthenticationSuccessHandler)
                .permitAll()
            .and()
                .logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .and()
                .csrf();
}

Solution 2 - Java

Spring security also very useful for providing authentication and authorization to the REST URLs. We no need to specify any custom implementations.

First, you need to specify the entry-point-ref to restAuthenticationEntryPoint in your security configuration as below.

 <security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >

	<security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
	<security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
	<security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
</security:http>

Implementation for the restAuthenticationEntryPoint might be as below.

 @Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
 
   public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
      response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
   }
}

After this you need to specify RequestHeaderAuthenticationFilter. It contains the RequestHeader key. This is basically used for identifying the user`s authentication. Generally RequestHeader carries this information while making the REST calls. For example consider below code

   <bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
    <property name="principalRequestHeader" value="Authorization"/>
    <property name="authenticationManager" ref="authenticationManager" />
  </bean>

Here,

<property name="principalRequestHeader" value="Authorization"/>

"Authorization" is the the key presented the incoming request. It holds the required user`s authentication information. Also you need to configure the PreAuthenticatedAuthenticationProvider to fulfill our requirement.

   <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
  <bean id="userDetailsServiceWrapper"
      class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
    <property name="userDetailsService" ref="authenticationService"/>
  </bean>
</property>
</bean>

This code will work for securing the REST urls by means of Authentication and authorization without any custom implementations.

For Complete code please find the below link:

https://github.com/srinivas1918/spring-rest-security

Solution 3 - Java

I searched this long time too.I am working on a similar project.I found out Spring has a module to implement session via redis. It looks easy and useful. I will add to my project too. Can be helpful:

http://docs.spring.io/spring-session/docs/1.2.1.BUILD-SNAPSHOT/reference/html5/guides/rest.html

Solution 4 - Java

To validate REST API there are 2 ways

1 - Basic authentication using default username and password set up in application.properties file

Basic Authentication

2 - Authenticate using database (userDetailsService) with the actual username and password

Advanced Authentication

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArtegonView Question on Stackoverflow
Solution 1 - JavaOleksandr LoushkinView Answer on Stackoverflow
Solution 2 - JavaNalla SrinivasView Answer on Stackoverflow
Solution 3 - Javaserkan ersoyView Answer on Stackoverflow
Solution 4 - JavaJeet Singh ParmarView Answer on Stackoverflow