© Balaji Varanasi and Maxim Bartkov 2022
B. Varanasi, M. BartkovSpring RESThttps://doi.org/10.1007/978-1-4842-7477-4_8

8. Security

Balaji Varanasi1   and Maxim Bartkov2
(1)
Salt Lake City, UT, USA
(2)
Kharkov, Ukraine
 
In this chapter we will discuss the following:
  • Strategies for securing REST services

  • OAuth 2.0

  • Basics of the Spring Security framework

  • Implementing QuickPoll security

Traditional web applications requiring security typically use username/passwords for identification purposes. REST services pose interesting security problems as they can be consumed by a variety of clients such as browsers and mobile devices. They can also be consumed by other services, and this machine-to-machine communication might not have any human interaction. It is also not uncommon for clients to consume REST services on behalf of a user. In this chapter, we will explore the different authentication/authorization approaches that can be used while working with REST services. Then we will look at using some of these approaches to secure our QuickPoll application.

Securing REST Services

We begin with a survey of six popular approaches that are used for securing REST services:
  • Session-based security

  • HTTP Basic authentication

  • Digest authentication

  • Certificate-based security

  • XAuth

  • OAuth

Session-Based Security

The session-based security model relies on a server side session to hold on to a user’s identity across requests. In a typical web application, when a user tries to access a protected resource, they are presented with a login page. On successful authentication, the server stores the logged-in user’s information in an HTTP session. On subsequent requests, the session is queried to retrieve the user’s information and is used to perform authorization checks. If the user doesn’t have the proper authorization, their request will be denied. Figure 8-1 is a pictorial representation of this approach.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig1_HTML.jpg
Figure 8-1

Session-based security flow

Frameworks such as Spring Security provide all the necessary plumbing to develop applications using this security model. This approach is very appealing to developers that are adding REST services to existing Spring Web applications. The REST services will retrieve the user identity from the session to perform authorization checks and serve resources accordingly. However, this approach violates the statelessness REST constraint. Also, because the server holds the client’s state, this approach is not scalable. Ideally, the client should hold the state and server should be stateless.

HTTP Basic Authentication

Using a login form to capture a username and password is possible when there is human interaction involved. However, this might not be possible when we have services talking to other services. HTTP Basic authentication provides a mechanism that allows clients to send authentication information using both interactive and noninteractive fashions.

In this approach, when a client makes a request to a protected resource, the server sends a 401 “Unauthorized” response code and a “WWW-Authenticate” header. The “Basic” portion of the header indicates that we will be using Basic authentication and the “realm” portion indicates a protected space on the server:
GET /protected_resource
401 Unauthorized
WWW-Authenticate: Basic realm="Example Realm"
On receiving the response, the client concatenates a username and password with a semicolon and Base64 encodes the concatenated string. It then sends that information over to the server using a standard Authorization header:
GET /protected_resource
Authorization: Basic bHxpY26U5lkjfdk
The server decodes the submitted information and validates the submitted credentials. On successful verification, the server completes the request. The entire flow is shown in Figure 8-2.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig2_HTML.jpg
Figure 8-2

HTTP Basic authentication flow

Because the client includes the authentication information in each request, the server becomes stateless. It is important to remember that the client is simply encoding the information and not encrypting it. Hence, on non-SSL/TLS connections, it is possible to conduct a man-in-the-middle attack and steal the password.

Digest Authentication

The Digest authentication approach is similar to the Basic authentication model discussed earlier except that the user credentials are sent encrypted. The client submits a request for a protected resource and the server responds with a 401 “Unauthorized” response code and a WWW-Authenticate header. Here is an example of a server response:
GET /protected_resource
401 Unauthorized
WWW-Authenticate: Digest realm="Example Realm", nonce="P35kl89sdfghERT10Asdfnbvc", qop="auth"
Notice that the WWW-Authenticate specifies the Digest authentication scheme along with a server-generated nonce and a qop. A nonce is an arbitrary token used for cryptographic purposes. The qop, or “quality of protection,” directive can contain two values—"auth" or "auth-int":
  • A qop value "auth" indicates that the digest is used for authentication purposes.

  • A value "auth-int" indicates that digest will be used for authentication and request integrity.

On receiving the request, if the qop value is set to "auth," the client generates a digest using this formula:
hash_value_1 = MD5(username:realm:password)
has_value_2 = MD5(request_method:request_uri)
digest = MD5(hash_value_1:nonce:hash_value_2)
If the qop value is set to "auth-int," the client computes the digest by including the request body:
hash_value_1 = MD5(username:realm:password)
has_value_2 = MD5(request_method:request_uri:MD5(request_body))
digest = MD5(hash_value_1:nonce:hash_value_2)
By default, the MD5 algorithm is used to compute the hash values. The digest is included in the Authorization header and is sent to the server. On receiving the request, the server computes the digest and verifies the user’s identity. On successful verification, the server completes the request. A complete flow of this method is shown in Figure 8-3.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig3_HTML.jpg
Figure 8-3

Digest authentication flow

The Digest authentication approach is more secure than the Basic authentication, as the password is never sent in clear text. However, on non-SSL/TLS communications, it is still possible for snoopers to retrieve the digest and replay the request. One way to address this problem is to limit server-generated nonces to one-time use only. Also, because the server has to generate the digest for verification, it needs to have access to the plain text version of the password. Hence, it can’t employ more secure one-way encryption algorithms such as bcrypt and can become more vulnerable to server side attacks.

Certificate-Based Security

The certificate-based security model relies on certificates to verify a party’s identity. In an SSL/TLS-based communication, a client such as a browser often verifies the server’s identity using certificates to ensure that the server is what it claims to be. This model can be extended to perform mutual authentication where a server can request a client certificate as part of an SSL/TLS handshake and verify a client’s identity.

In this approach, on receiving a request for a protected resource, the server presents its certificate to the client. The client ensures that a trusted Certificate Authority (CA) issued the server’s certificate and sends its certificate over to the server. The server verifies the client’s certificate and, on successful verification, will grant access to the protected resource. This flow is shown in Figure 8-4.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig4_HTML.jpg
Figure 8-4

Certificate-based security flow

The certificate-based security model eliminates the need to send over a shared secret, making it more secure over username/password models. However, deployments and maintenance of certificates can be expensive and typically are used for large systems.

XAuth

As REST APIs became popular, the number of third-party applications that use those APIs also grew significantly. These applications need a username and password in order to interact with REST services and perform actions on behalf of users. This poses a huge security problem as third-party applications now have access to usernames and passwords. A security breach in the third-party application can compromise user information. Also, if the user changes his credentials, he needs to remember to go and update all of these third-party applications. Finally, this mechanism doesn’t allow the user to revoke his authorization to the third-party application. The only option for revoking in this case would be to change his password.

The XAuth and OAuth schemes provide a mechanism to access protected resources on a user’s behalf without needing to store passwords. In this approach, a client application would request a username and password from the user typically by using a login form. The client would then send the username and password to the server. The server receives the user’s credentials and validates them. On successful validation, a token is returned to the client. The client discards the username and password information and stores the token locally. When accessing a user’s protected resource, the client would include the token in the request. This is typically accomplished using a custom HTTP header such as X-Auth-Token. The longevity of the token is dependent on the implementing service. The token can remain until the server revokes it or the token can expire in a designated period of time. This flow is shown in Figure 8-5.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig5_HTML.png
Figure 8-5

XAuth security flow

Applications such as Twitter allow third-party applications to access their REST API using an XAuth scheme. However, even with XAuth, a third-party application needs to capture a username and password, leaving the possibility of misuse. Considering the simplicity involved in XAuth, it might be a good candidate when the same organization develops the client as well as the REST API.

OAuth 2.0

The Open Authorization or OAuth is a framework for accessing protected resources on behalf of a user without storing a password. The OAuth protocol was first introduced in 2007 and was superseded by OAuth 2.0, which was introduced in 2010. In this book, we will be reviewing OAuth 2.0 and general principles.

OAuth 2.0 defines the following four roles:
  • Resource owner—A resource owner is the user that wants to give access to portions of their account or resources. For example, a resource owner could be a Twitter or a Facebook user.

  • Client—A client is an application that wants access to a user’s resources. This could be a third-party app such as Klout (https://klout.com/) that wants to access a user’s Twitter account.

  • Authorization server—An authorization server verifies the user’s identity and grants the client a token to access the user’s resources.

  • Resource server—A resource server hosts protected user resources. For example, this would be Twitter API to access tweets and timelines and so on.

The interactions between these four roles discussed are depicted in Figure 8-6. OAuth 2.0 requires these interactions to be conducted on SSL.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig6_HTML.png
Figure 8-6

OAuth 2.0 security flow

Before a client can participate in the “OAuth dance” shown in Figure 8-6, it must register itself with the authorization server. For most public APIs such as Facebook and Twitter, this involves filling out an application form and providing information about the client such as application name, base domain, and website. On successful registration, the client will receive a Client ID and a Client secret. The Client ID is used to uniquely identify the Client and is available publicly. These client credentials play an important part in the OAuth interactions, which we will discuss in just a minute.

The OAuth interaction begins with the user expressing interest in using the “Client,” a third-party application. The client requests authorization to access protected resources on the user’s behalf and redirects the user/resource owner to the authorization server. An example URI that the client can redirect the user to is shown here:
https://oauth2.example.com/authorize?client_id=CLIENT_ID&response_type=auth_code&call_back=CALL_BACK_URI&scope=read,tweet

The usage of HTTPS is mandatory for any production OAuth 2.0 interactions, and, hence, the URI begins with https. The CLIENT_ID is used to provide the client’s identity to the authorization server. The scope parameter provides a comma-separated set of scopes/roles that the client needs.

On receiving the request, the authorization server would present the user with an authentication challenge typically via a login form. The user provides his username and password. On successful verification of the user credentials, the authorization server redirects the user to the client application using the CALL_BACK_URI parameter. The authorization server also appends an authorization code to the CALL_BACK_URI parameter value. Here is an example URL that an authorization server might generate:
https://mycoolclient.com/code_callback?auth_code=6F99A74F2D066A267D6D838F88
The client then uses the authorization code to request an access token from the authorization server. To achieve this, a client would typically perform an HTTP POST on a URI like this:
https://oauth2.example.com/access_token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&
auth_code=6F99A74F2D066A267D6D838F88
As you can see, the client provides its credentials as part of the request. The authorization server verifies the client’s identity and authorization code. On successful verification, it returns an access token. Here is an example response in JSON format:
{"access_token"="f292c6912e7710c8"}

On receiving the access token, the client will request a protected resource from the resource server passing in the access token it obtained. The resource server validates the access token and serves the protected resource.

OAuth Client Profiles

One of the strengths of OAuth 2.0 is its support for a variety of client profiles such as “web application,” “native application,” and “user agent/browser application.” The authorization code flow discussed earlier (often referred to as authorization grant type ) is applicable to “web application” clients that have a web-based user interface and a server side backend. This allows the client to store the authorization code in a secure backend and reuse it for future interactions. Other client profiles have their own flows that determine the interaction between the four OAuth 2.0 players.

A pure JavaScript-based application or a native application can’t store authorization codes securely. Hence, for such clients, the callback from the authorization server doesn’t include an authorization code. Instead, an implicit grant-type approach is taken and an access token is directly handed over to the client, which is then used for requesting protected resources. Applications falling under this client profile will not have a client secret and are simply identified using the client ID.

OAuth 2.0 also supports an authorization flow, referred to as password grant type that is similar to XAuth discussed in the previous section. In this flow, the user supplies his credentials to the client application directly. He is never redirected to the authorization server. The client passes these credentials to the authorization server and receives an access token for requesting protected resources.

OAuth 1.0 introduced several implementation complexities especially around the cryptographic requirements for signing requests with client credentials. OAuth 2.0 simplified this by eliminating signatures and requiring HTTPS for all interactions. However, because many of OAuth 2’s features are optional, the specification has resulted in non-interoperable implementations.

Refresh Tokens versus Access Tokens

The lifetime of access tokens can be limited and clients should be prepared for the possibility of a token no longer working. To prevent the need for the resource owner to repeatedly authenticate, the OAuth 2.0 specification has provided a notion of refresh tokens. An authorization server can optionally issue a refresh token when it generates an access token. The client stores this refresh token, and when an access token expires, it contacts the authorization server for a fresh set of access token as well as refresh token. Specification allows generation of refresh tokens for authorization and password grant-type flows. Considering the lack of security with the “implicit grant type,” refresh tokens are prohibited for such client profiles.

Spring Security Overview

To implement security in the QuickPoll application, we will be using another popular Spring subproject, namely, Spring Security. Before we move forward with the implementation, let’s understand Spring Security and the different components that make up the framework.

Spring Security, formerly known as Acegi Security, is a framework for securing Java-based applications. It provides an out-of-the-box integration to a variety of authentication systems such as LDAP, Kerberos, OpenID, OAuth, and so on. With minimal configuration, it can be easily extended to work with any custom authentication and authorization systems. The framework also implements security best practices and has inbuilt features to protect against attacks such as CSRF, or Cross-Site Request Forgery, session fixation, and so on.

Spring Security provides a consistent security model that can be used to secure web URLs and Java methods. The high-level steps involved during the Spring Security authentication/authorization process along with components involved are listed here:
  1. 1.

    The process begins with a user requesting a protected resource on a Spring-secured web application.

     
  2. 2.

    The request goes through a series of Spring Security filters referred to as a “filter chain” that identify an org.springframework.security.web.AuthenticationEntryPoint to service the request. The AuthenticationEntryPoint will respond to the client with a request to authentication. This is done, for example, by sending a login page to the user.

     
  3. 3.

    On receiving authentication information from the user such as a username/password, a org.springframework.security.core.Authentication object is created. The Authentication interface is shown in Listing 8-1, and its implementation plays a dual role in Spring Security. They represent a token for an authentication request or a fully authenticated principal after authentication is successfully completed. The isAuthenticated method can be used to determine the current role played by an Authentication instance. In case of a username/password authentication, the getPrincipal method returns the username and the getCredentials returns the password. The getUserDetails method contains additional information such as IP address and so on.

     
public interface Authentication extends Principal, Serializable {
        Object getPrincipal();
        Object getCredentials();
        Object getDetails();
        Collection<? extends GrantedAuthority> getAuthorities();
        boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
Listing 8-1

Authentication API

  1. 4.

    As a next step, the authentication request token is presented to an org.springframework.security.authentication.AuthenticationManager. The AuthenticationManager, as shown in Listing 8-2, contains an authenticate method that takes an authentication request token and returns a fully populated Authentication instance. Spring provides an out-of-the-box implementation of AuthenticationManager called ProviderManager.

     
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
Listing 8-2

AuthenticationManager API

  1. 5.

    In order to perform authentication, the ProviderManager needs to compare the submitted user information with a backend user store such as LDAP or database. ProviderManager delegates this responsibility to a series of org.springframework.security.authentication.AuthenticationProvider. These AuthenticationProviders use an org.springframework.security.core.userdetails.UserDetailsService to retrieve user information from backend stores. Listing 8-3 shows the UserDetailsService API.

     
public interface UserDetailsService {
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException;
}
Listing 8-3

UserDetailsService API

Implementations of UserDetailsService such as JdbcDaoImpl and LdapUserDetailService will use the passed-in username to retrieve user information. These implementations will also create a set of GrantedAuthority instances that represent roles/authorities the user has in the system.
  1. 6.

    The AuthenticationProvider compares the submitted credentials with the information in the backend system, and on successful verification, the org.springframework.security.core.userdetails.UserDetails object is used to build a fully populated Authentication instance.

     
  2. 7.

    The Authentication instance is then put into an org.springframework.security.core.context.SecurityContextHolder. The SecurityContextHolder , as the name suggests, simply associates the logged-in user’s context with the current thread of execution so that it is readily available across user requests or operations. In a web-based application, the logged-in user’s context is typically stored in the user’s HTTP session.

     
  3. 8.

    Spring Security then performs an authorization check using an org.springframework.security.access.intercept.AbstractSecurityInterceptor and its implementations org.springframework.security.web.access.intercept.FilterSecurityInterceptor and org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor. The FilterSecurityInterceptor is used for URL-based authorization and MethodSecurityInterceptor is used for method invocation authorization.

     
  4. 9.

    The AbstractSecurityInterceptor relies on security configuration and a set of org.springframework.security.access.AccessDecisionManagers to decide if the user is authorized or not. On successful authorization, the user is given access to the protected resource.

     
Note

To keep things simple, I have purposefully omitted some Spring Security classes in these steps. For a complete review of Spring Security and the authentication/authorization steps, please refer to Pro Spring Security (Apress, 2019).

Now that you have a basic understanding of Spring Security’s authentication/authorization flow as well as some of its components, let’s look at integrating Spring Security into our QuickPoll application.

Securing QuickPoll

We will implement security in the QuickPoll application to meet the following two requirements:
  • Registered users can create and access polls. This allows us to keep track of accounts, usage, and so on.

  • Polls can be deleted only by users with admin privileges

Note

In this chapter, we will continue building on the work that we did on the QuickPoll application in the previous chapters. Alternatively, a starter project is available for you to use inside the Chapter8starter folder of the downloaded source code. In this chapter, we will secure QuickPoll using Basic authentication. Then we will add OAuth 2.0 support to QuickPoll. Hence, the Chapter8final folder contains two folders: quick-poll-ch8-final-basic-auth and quick-poll-ch8-final. The quick-poll-ch8-final-basic-auth contains the solution with Basic authentication added to QuickPoll. The quick-poll-ch8-final contains the completed solution with both Basic authentication and OAuth 2.0 added. We understand that not all projects need OAuth 2.0 support. Hence, splitting the final solution into two projects allows you to examine and use features/code that you need. Please refer to the solutions under the final folder for complete listings containing getters/setters and additional imports. The downloaded Chapter8 folder also contains an exported Postman collection containing REST API requests associated with this chapter.

By requiring user authentication, we will be drastically changing the behavior of the QuickPoll application. To allow existing users to continue using our QuickPoll application, we will create a new version (v3) of our API to implement these changes. To accomplish this, create a new com.apress.v3.controller package under srcmainjava and copy controllers from the com.apress.v2.controller package. For the newly copied controllers, change the RequestMappings from “/v2/” to “/v3/” and change the controller name prefixes from v2 to v3 to reflect version 3 of the API. We start the implementation by adding the Spring Security starter dependency shown in Listing 8-4 to QuickPoll project’s pom.xml file . This would bring in all Spring Security–related JAR files into the project.
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Listing 8-4

Spring Starter POM

On seeing Spring Security in the classpath, Spring Boot adds default security configuration that secures all of the HTTP endpoints with an HTTP basic authentication. Start the QuickPoll application, and submit a GET request to http://localhost:8080/v3/polls using Postman. Postman displays an authentication window prompting you to enter a username and password, as shown in Figure 8-7.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig7_HTML.jpg
Figure 8-7

Basic authentication window in Postman

Spring Boot’s default security configuration comes with a single user with username user. Spring Boot generates a random password for the user and prints it at INFO log level during application startup. In your console/log file, you should see an entry like this one:
Using default security password: 554cc6c2-67e1-4f1e-8c5b-096609e2d0b1

Enter the username and password found in your console into the Postmaster login window and hit Log In. Spring Security will validate the entered credentials and allow the request to be completed.

cURL

Up to this point, we have been using Postman for testing our QuickPoll application. In this chapter, we will be using a command line tool named cURL in conjunction with Postman. cURL is a popular open-source tool used for interacting with servers and transferring data with URL syntax. It comes installed in most operating system distributions. If cURL is not available on your system, follow the instructions at http://curl.haxx.se/download.html to download and install cURL on your machine. Refer to Appendix A for instructions on installing cURL on a Windows machine.

To test our QuickPoll Basic authentication using cURL, run the following command at command line:
curl -vu user:554cc6c2-67e1-4f1e-8c5b-096609e2d0b1 http://localhost:8080/v3/polls

In this command, the –v option requests cURL to run in the debug mode (verbose). The –u option allows us to specify the username and password needed for Basic authentication. A full list of cURL options is available at http://curl.haxx.se/docs/manual.html.

User Infrastructure Setup

Although Spring Boot has simplified Spring Security integration significantly, we would like to customize security behavior so that it uses application users instead of Spring Boot’s generic user. We also would like to apply the security to the v3 PollController, leaving other endpoints to be accessed anonymously. Before we look at customizing Spring Security, let’s set up the infrastructure needed for creating/updating QuickPoll application users.

We start by creating a User domain object as shown in Listing 8-5 to represent a QuickPoll user. The User class contains attributes such as username, password, firstname, and lastname. It also contains a Boolean flag to indicate if the user has administrative privileges. As a security best practice, we have annotated the password field with @JsonIgnore. Therefore, the password field will not be included in a user’s representation, thereby preventing clients from accessing the password value. Because “User” is a keyword in databases such as Oracle, we have used the @Table annotation to give the name “Users” to table corresponding to this User entity.
package com.apress.domain;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.Type;
import javax.validation.constraints.NotEmpty;
@Entity
@Table(name="USERS")
public class User {
        @Id
        @GeneratedValue
        @Column(name="USER_ID")
        private Long id;
        @Column(name="USERNAME")
        @NotEmpty
        private String username;
        @Column(name="PASSWORD")
        @NotEmpty
        @JsonIgnore
        private String password;
        @Column(name="FIRST_NAME")
        @NotEmpty
        private String firstName;
        @Column(name="LAST_NAME")
        @NotEmpty
        private String lastName;
        @Column(name="ADMIN", columnDefinition="char(3)")
        @Type(type="yes_no")
        @NotEmpty
        private boolean admin;
        // Getters and Setters omitted for brevity
}
Listing 8-5

User Class

We will be storing the QuickPoll users in a database and hence will require a UserRepository to perform CRUD actions on the User entity. Listing 8-6 shows the UserRepository interface created under com.apress.repository package. In addition to the finder methods provided by the CrudRepository, the UserRepository contains a custom finder method named findByUsername. Spring Data JPA would provide a runtime implementation so that the findByUsername method retrieves a user associated with the passed-in username parameter.
package com.apress.repository;
import org.springframework.data.repository.CrudRepository;
import com.apress.domain.User;
public interface UserRepository extends CrudRepository<User, Long> {
        public User findByUsername(String username);
}
Listing 8-6

UserRepository Interface

Applications such as QuickPoll typically have an interface that allows new users to register. To keep things simple for the purposes of this book, we have generated some test users shown in Listing 8-7. Copy these SQL statements to the end of import.sql file under the QuickPoll project’s srcmain esources folder. When the application gets bootstrapped, Hibernate will load these test users into the “Users” table and make them available for the application’s use.
insert into users (user_id, username, password, first_name, last_name, admin) values
(1, 'mickey', '$2a$10$kSqU.ek5pDRMMK21tHJlceS1xOc9Kna4F0DD2ZwQH/LAzH0ML0p6.', 'Mickey', 'Mouse', 'no');
insert into users (user_id, username, password, first_name, last_name, admin) values
(2, 'minnie', '$2a$10$MnHcLn.XdLx.iMntXsmdgeO1B4wAW1E5GOy/VrLUmr4aAzabXnGFq', 'Minnie', 'Mouse', 'no');
insert into users (user_id, username, password, first_name, last_name, admin) values
(3, 'donald', '$2a$10$0UCBI04PCXiK0pF/9kI7.uAXiHNQeeHdkv9NhA1/xgmRpfd4qxRMG', 'Donald', 'Duck', 'no');
insert into users (user_id, username, password, first_name, last_name, admin) values
(4, 'daisy', '$2a$10$aNoR88g5b5TzSKb7mQ1nQOkyEwfHVQOxHY0HX7irI8qWINvLDWRyS', 'Daisy', 'Duck', 'no');
insert into users (user_id, username, password, first_name, last_name, admin) values
(5, 'clarabelle', '$2a$10$cuTJd2ayEwXfsPdoF5/hde6gzsPx/gEiv8LZsjPN9VPoN5XVR8cKW', 'Clarabelle', 'Cow', 'no');
insert into users (user_id, username, password, first_name, last_name, admin) values
(6, 'admin', '$2a$10$JQOfG5Tqnf97SbGcKsalz.XpDQbXi1APOf2SHPVW27bWNioi9nI8y', 'Super', 'Admin', 'yes');
Listing 8-7

Test User Data

Notice that the password for the generated test users is not in plain text. Following good security practices, I have encrypted the password values using the BCrypt (http://en.wikipedia.org/wiki/Bcrypt) adaptive hashing function. Table 8-1 shows these test users and their plain text version of passwords.
Table 8-1

Test User Information

Username

Password

Is admin

Mickey

Cheese

No

Minnie

Red01

No

Donald

Quack

No

Daisy

Quack2

No

Clarabelle

Moo

No

Admin

Admin

Yes

UserDetailsService Implementation

In the Spring Security introduction section, we learned that a UserDetailsService is typically used to retrieve user information, which gets compared with user-submitted credentials during the authentication process. Listing 8-8 shows a UserDetailsService implementation for our QuickPoll application.
package com.apress.security;
import javax.inject.Inject;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.apress.domain.User;
import com.apress.repository.UserRepository;
@Component
public class QuickPollUserDetailsService implements UserDetailsService {
        @Inject
        private UserRepository userRepository;
        @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                User user = userRepository.findByUsername(username);
                if(user == null) {
throw new UsernameNotFoundException(String.format("User with the username %s doesn't exist", username));
                }
                // Create a granted authority based on user's role.
// Can't pass null authorities to user. Hence initialize with an
empty arraylist
                List<GrantedAuthority> authorities = new ArrayList<>();
                if(user.isAdmin()) {
                        authorities = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
                }
                // Create a UserDetails object from the data
UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
                return userDetails;
        }
}
Listing 8-8

UserDetailsService Implementation for QuickPoll

The QuickPollUserDetailsService class makes use of UserRepository to retrieve User information from the database. It then checks if the retrieved user has administrative rights and constructs an admin GrantedAuthority, namely, ROLE_ADMIN. The Spring Security infrastructure expects the loadUserByUsername method to return an instance of type UserDetails. Hence, the QuickPollUserDetailsService class creates the o.s.s.c.u.User instance and populates it with the data retrieved from the database. The o.s.s.c.u.User is a concrete implementation of the UserDetails interface. If the QuickPollUserDetailsService can’t find a user in the database for the passed-in username, it will throw a UsernameNotFoundException exception.

Customizing Spring Security

Customizing Spring Security’s default behavior involves creating a configuration class that is annotated with @EnableWebSecurity. This configuration class typically extends the org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurer class that provides helper methods to simplify our security configuration. Listing 8-9 shows the SecurityConfig class that will contain security-related configuration for QuickPoll application.
package com.apress;
import javax.inject.Inject;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Inject
        private UserDetailsService userDetailsService;
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.userDetailsService(userDetailsService)
                        .passwordEncoder(new BCryptPasswordEncoder());
        }
}
Listing 8-9

Security Configuration for QuickPoll

The SecurityConfig class declares a userDetailsService property, which gets injected with a QuickPollUserDetailsService instance at runtime. It also overrides a super class’s configure method that takes an AuthenticationManagerBuilder as parameter. The AuthenticationManagerBuilder is a helper class implementing the Builder pattern that provides an easy way of assembling an AuthenticationManager. In our method implementation, we use the AuthenticationManagerBuilder to add the UserDetailsService instance. Because we have encrypted the passwords stored in the database using BCrypt algorithm, we provide an instance of BCryptPasswordEncoder. The authentication manager framework will use the password encoder to compare the plain string provided by the user with the encrypted hash stored in the database.

With this configuration in place, restart the QuickPoll application and run the following command at the command line:
curl -u mickey:cheese http://localhost:8080/v2/polls
If you run the command without the –u option and the username/password data, you will receive a 403 error from the server as shown here:
{"timestamp":1429998300969,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/v2/polls"}

Securing URI

The SecurityConfig class introduced in the previous section gets us one step closer by configuring HTTP Basic authentication to use QuickPoll users. This configuration, however, protects all endpoints and requires authentication to access resources. To implement our requirement to just secure v3 Poll API, we will override another WebSecurityConfigurer’s config method . Listing 8-10 shows the config method implementation that needs to be added to the SecurtyConfig class.
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
@Override
protected void configure(HttpSecurity http) throws Exception {
        http
          .sessionManagement()
             .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
             .and()
          .authorizeRequests()
             .antMatchers("/v1/**", "/v2/**", "/swagger-ui/**", "/api-docs/**").permitAll()
             .antMatchers("/v3/polls/ **").authenticated()
             .and()
          .httpBasic()
             .realmName("Quick Poll")
             .and()
          .csrf()
             .disable();
}
Listing 8-10

New Config Method in SecurityConfig

The HttpSecurity parameter passed into the config method in Listing 8-10 allows us to specify the URI that should be secured or unsecured. We begin the method implementation by requesting Spring Security to not create an HTTP session and not store logged-in user’s SecurityContext in the session. This is achieved using the SessionCreationPolicy.STATELESS creation policy. We then use antMatchers to provide Ant-style URI expressions that we don’t want Spring Security protecting. Using the permitAll method , we are specifying that the API versions 1 and 2 and Swagger UI should be available anonymously. The next antMatchers along with authenticated method specifies that Spring Security should only allow authenticated users to access V3 Polls API. Finally, we enable HTTP Basic authentication and set the realm name to “Quick Poll.” Restart QuickPoll application and you should be prompted for authentication only on the /v3/polls resources.

Note

Cross-Site Request Forgery, or CSRF (http://en.wikipedia.org/wiki/Cross-site_request_forgery), is a type of security vulnerability whereby a malicious website forces the end user to execute unwanted commands on a different website in which they are currently authenticated. Spring Security by default enables CSRF protection and highly recommends using it for requests submitted by a user via a browser. For services that are used by nonbrowser clients, the CSRF can be disabled. By implementing custom RequestMatchers, it is possible to disable CSRF only for certain URLs or HTTP methods.

To keep things simple and manageable for this book, we have disabled CSRF protection.

The last security requirement that we have is to ensure that only users with administrative privileges can delete a poll. To implement this authorization requirement, we will apply Spring Security’s method level security on the deletePoll method . Spring’s method level security can be enabled using the aptly named org.springframework.security.config.annotation.method.configuration. EnableGlobalMethodSecurity annotation . Listing 8-11 shows the annotation added to the SecurityConfig class.
package com.apress;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebMvcConfigurer {
        // Content removed for brevity
}
Listing 8-11

EnableGlobalMethodSecurity Annotation Added

Spring Security supports a rich set of class and method-level authorization annotations along with standard-based JSR 250 annotation security. The prePostEnabled flag in EnableGlobalMethodSecurity requests Spring Security to enable annotations that perform pre- and post-method invocation authorization checks. The next step is to annotate the v3 PollController’s deletePoll method with @PreAuthorize annotation as shown in Listing 8-12.
import org.springframework.security.access.prepost.PreAuthorize;
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public ResponseEntity<Void> deletePoll(@PathVariable Long pollId) {
        // Code removed for brevity
}
Listing 8-12

PreAuthorize Annotation Added

The @PreAuthorize annotation decides if the deletePoll method can be invoked or not. Spring Security makes this decision by evaluating the Spring-EL expression passed in as the annotation’s value. In this case, the hasAuthority checks if the logged-in user has the “ROLE_ADMIN” authority. Restart the application and perform a DELETE on the endpoint http://localhost:8080/v3/polls/12 using Postman. When prompted for credentials, enter the username mickey and the password cheese, and hit Log In. Figure 8-8 shows the request and associated inputs.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig8_HTML.png
Figure 8-8

Deleting poll with unauthorized users

Since the user mickey doesn’t have administrative rights, you will see an unauthorized response from the service, as shown in Figure 8-9.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig9_HTML.png
Figure 8-9

Unauthorized delete response

Now let’s retry this request using an admin user with administrative rights. In Postman, click the Basic Auth tab and enter the credentials admin/admin and hit “Refresh headers” as shown in Figure 8-10. On submitting the request, you should see the Poll resource with ID 12 deleted.
../images/332520_2_En_8_Chapter/332520_2_En_8_Fig10_HTML.jpg
Figure 8-10

Basic Auth admin credentials in Postman

To delete a Poll using cURL, run the following command:
curl -i -u admin:admin  -X DELETE http://localhost:3/v3/polls/13
The previously mentioned command deletes a Poll resource with ID 13. The –i option requests curl to output the response headers. The –X option allows us to specify the HTTP method name. In our case, we specified the DELETE HTTP method. The output of this result is shown in Listing 8-13.
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
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-Length: 0
Date: Sat, 25 Apr 2015 21:50:35 GMT
Listing 8-13

Output of cURL Delete

Summary

Security is an important aspect of any enterprise application. In this chapter, we reviewed strategies for securing REST services. We also took a deeper look into OAuth 2 and reviewed its different components. We then used Spring Security to implement Basic authentication in our QuickPoll application. In the next chapter, we will use Spring’s RestTemplate to build REST clients. We will also use the Spring MVC Test framework to perform unit and integration testing on REST controllers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.213.44