introduce

In this article, we’ll learn how to use Spring and Spring Security 5 to provide a more secure RESTful API. We will use Java configuration to set up security and will use login and Cookie methods for authentication.

Enable the Spring Security

Spring Security’s architecture is entirely based on Servlet filters. The easiest option to register a Spring Security filter is to add @enableWebSecurity annotations to our config class:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
 
    // ...
}
Copy the code

For the Spring Boot applications, we can expand AbstractSecurityWebApplicationInitializer and transfer in its constructor config class:

public class SecurityWebApplicationInitializer 
  extends AbstractSecurityWebApplicationInitializer {
 
    public SecurityWebApplicationInitializer(a) {
        super(SecurityJavaConfig.class); }}Copy the code

Or we can declare it in the application’s web.xml:

<filter>
   <filter-name>springSecurityFilterChain</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
   <filter-name>springSecurityFilterChain</filter-name>
   <url-pattern>/ *</url-pattern>
</filter-mapping>
Copy the code

We should filter named “springSecurityFilterChain”, to match the container Spring Security to create the default bean. Note that the defined filter is not the actual class that implements the security logic. Instead, it is a delegate FilterProxy that delegates the filter’s methods to the internal bean. This is done so that the target bean can still benefit from the lifecycle and flexibility of the Spring context.

Spring Security Java configuration

We can completely security configuration in the Java class, method is to create a configuration class, expanded the WebSecurityConfigurerAdapter with @ EnableWebSecurity to note:

@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
 
    // ...
}
Copy the code

Now let’s create users in SecurityJavaConfig in different roles that we will use to validate our API endpoint:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
        .and()
        .withUser("user").password(encoder().encode("userPass")).roles("USER");
}
 
@Bean
public PasswordEncoder  encoder(a) {
    return new BCryptPasswordEncoder();
}
Copy the code

Next, let’s configure security for our API endpoint:

@Override
protected void configure(HttpSecurity http) throws Exception { 
    http
    .csrf().disable()
    .exceptionHandling()
    .authenticationEntryPoint(restAuthenticationEntryPoint)
    .and()
    .authorizeRequests()
    .antMatchers("/api/foos").authenticated()
    .antMatchers("/api/admin/**").hasRole("ADMIN")
    .and()
    .formLogin()
    .successHandler(mySuccessHandler)
    .failureHandler(myFailureHandler)
    .and()
    .logout();
}
Copy the code

Http

In our code implementation, we use antMatchers to create secure mappings/API /foos and/API /admin/**. Any authenticated user can access/API /foos. On the other hand, / API /admin/** can only be accessed by admin role users. In a standard Web application, the authentication process can be triggered automatically when an unauthenticated client tries to access a protected resource. This process typically redirects to the login page so that the user can enter credentials. However, for REST Web services, this behavior doesn’t make much sense. We should be able to authenticate only by requests with the correct URI, and if the user is not authenticated, all requests should fail with a 401 unauthorized status code. Spring Security uses the concept of EntryPoint to handle the automatic triggering of the authentication process — a required part of the configuration that can be injected through the authenticationEntryPoint method. Simply return 401 when triggered:

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

A login form for REST

There are several ways to authenticate REST apis. One of the defaults provided by Spring Security is form login, It USES the authentication processing filter org. Springframework. Security. Web. Authentication. UsernamePasswordAuthenticationFilter. The formLogin element creates this filter and provides additional methods successHandler and failureHandler to set up our custom authentication success and failure handlers, respectively.

Authentication should return 200 instead of 301

By default, a form login will answer a successful authentication request with a 301 status code, which makes sense in the context of the actual login form that needs to be redirected after the login. However, for RESTful Web services, the response required for successful validation should be 200 OK. To do this, we inject a custom authentication success handler in the form login filter, replacing the default handler. The new handler with the default org. Springframe. Security. Web. Exactly the same as the login authentication. SavedRequestAwareAuthenticationSuccessHandler has an obvious difference – it delete the redirect logic:

public class MySavedRequestAwareAuthenticationSuccessHandler 
  extends SimpleUrlAuthenticationSuccessHandler {
 
    private RequestCache requestCache = new HttpSessionRequestCache();
 
    @Override
    public void onAuthenticationSuccess( HttpServletRequest request, HttpServletResponse response, Authentication authentication) 
      throws ServletException, IOException {
  
        SavedRequest savedRequest
          = requestCache.getRequest(request, response);
 
        if (savedRequest == null) {
            clearAuthenticationAttributes(request);
            return;
        }
        String targetUrlParam = getTargetUrlParameter();
        if(isAlwaysUseDefaultTargetUrl() || (targetUrlParam ! =null
          && StringUtils.hasText(request.getParameter(targetUrlParam)))) {
            requestCache.removeRequest(request, response);
            clearAuthenticationAttributes(request);
            return;
        }
 
        clearAuthenticationAttributes(request);
    }
 
    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache; }}Copy the code

Authentication manager and provider

The authentication process uses an in-memory provider to perform authentication. We created two users, USER with the USER role and ADMIN with the ADMIN role.

Finally – authenticate against the running REST service

Now, let’s look at how to authenticate against the REST API for different users. The login URL is /login. A simple curl command is used to login to the user named user and password userPass.

curl -i -X POST -d username=user -d password=userPass
http://localhost:8080/spring-security-rest/login
Copy the code

This request will return a Cookie that we can use for any subsequent request to the REST service. We can use curl to authenticate and save the cookies it receives in a file:

curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt 
http://localhost:8080/spring-security-rest/login
Copy the code

We can then use the cookie ** in the ** file to make further authentication requests:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt 
http://localhost:8080/spring-security-rest/api/foos
Copy the code

Since the user has access to the/API /foos/* endpoint, this authenticated request will correctly get a * 200 OK * :

HTTP/1.1 200 OK Server: apache-coyote /1.1 Content-Type: Application /json; charset=UTF-8 Transfer-Encoding: chunked Date: Wed, 24 Jul 2013 20:31:13 GMT [{"id":0,"name":"qulingfeng"}]Copy the code

Curl curl curl curl curl curl curl curl curl curl

curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt 
http://localhost:8080/spring-security-rest/login
Copy the code

Then update cookies access management endpoint/API /admin/* :

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt 
http://localhost:8080/spring-security-rest/api/admin/x
Copy the code

Since the admin user has access to the endpoint/API /admin/*, all responses were successful:

HTTP/1.1 200 OK Server: apache-coyote /1.1 x-Content-type-options: nosniffing X-xss-protection: 1; mode=block Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 X-Frame-Options: DENY Content-Type: application/json; charset=ISO-8859-1 Content-Length: 5 Date: Mon, 15 Oct 2018 17:16:39 GMT HelloCopy the code

The XML configuration

We can also use XML instead of Java configuration to do all of the above security configuration:

<http entry-point-ref="restAuthenticationEntryPoint">
    <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/>
 
    <form-login
      authentication-success-handler-ref="mySuccessHandler"
      authentication-failure-handler-ref="myFailureHandler" />
 
    <logout />
</http>
 
<beans:bean id="mySuccessHandler"
  class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/>
<beans:bean id="myFailureHandler" class=
  "org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>
 
<authentication-manager alias="authenticationManager">
    <authentication-provider>
        <user-service>
            <user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
            <user name="user" password="userPass" authorities="ROLE_USER"/>
        </user-service>
    </authentication-provider>
</authentication-manager>
Copy the code

conclusion

In this tutorial, we covered the basic security configuration and implementation of RESTful services using Spring Security 5. We learned how to configure security for REST apis entirely through Java configuration and looked at its Web.xml configuration alternatives. Next, we discussed how to create users and roles for the protected application and how to map these users to specific endpoints of the application. Finally, we looked at how to create custom authentication entry points and custom success handlers to give the application more flexibility in controlling security.

Welcome to follow my official account: Qulingfeng, for exclusive learning resources and daily dry goods push. If you are interested in my feature content, you can also follow my blog: sagowiec.com