4. Security
1. HTTPS
Nowadays, we need to add all we can do to secure our applications. We are going to study about tokens in order to authorize and authenticate our request, but we need an extra layer, https.
In this webpage https://tiptopsecurity.com/how-does-https-work-rsa-encryption-explained/ you can find how https works
1.1. Certificate
Firstly, we need to generate certificates, or buy it. We will use keytool tool included with java development kit in order to generate. This command generates a pair (public and private) certificate.
| Bash | |
|---|---|
After running this command, we must answer about who we are, as follows:
1.2. Configure Spring
Once the process has finished, we must add the certificate inside our project. For instance inside /resources/keystore. Finally, we must load the certificate and enable SSL, simply adding this lines on application.properties:
And it is all, when Spring starts we will see that is working with https protocol:
| Bash | |
|---|---|
and in the request probably browsers will not trust with our certificate (we must add a confidence exception):
2. Spring Security
Spring Security is an umbrella project that group all mechanisms referent to security. We need to add:
| XML | |
|---|---|
and magically:
- The default configuration is enabled, through a filter, called
SpringSecurityFilterChain. - A bean of type
UserDetailsServiceis created with a user named user and a random password that is printed by the console. - The filter is registered in the servlet container for all requests.
Although you have not configured much, it has many consequences:
- Requires authentication to interact with our application
- Generates a default login form.
- Generates a logout mechanism
- Protect password storage with
BCrypt. - Provides against CSRF attacks, Session Fixation, Clickjacking...
We can create a configuration class, with this content:
Note
We will come back to this class to add more configurations. Most interesting is method configure, and creation of several Beans used by another classes.
2.1. Users Data for Authentication and Authorization (Models)
Important
From now, this sample is based from a famous webpage https://www.bezkoder.com. We explain all necessary from the sample who you can view here.
In this section we are going to prepare our application to identify users, with several roles. With these roles, we could grant access or not to several resources.
2.2. User & Roles
To do this, we net to create a User class, to store this information in our database. This user could have a collection of roles. We can do it with a many-to-many relationship.
based on this enumeration, we will do a Role class to store the roles that our application will support:
| Java | |
|---|---|
finally, then a User class as follows. We add new annotations of validation:
2.3. User & Roles Repository
We need to create our repos about last entities, creating interfaces like we normally create.
We also add methods to check User existance by name and email, and methods to find by name, either User and Role
2.4. UserDetails
Spring needs that someone implements UserDetails interface, very important because Spring Security will use a UserDetails. UserDetails contains necessary information to build an Authentication object from DAOs or other source of security data. We create a class, called UserDetailsImpl, who:
- Must have
usernameandpasssordfields, and getters. You must respect names, any changes is prohibited. These methods will be used by authentication classes. - Override methods from
UserDetails, for instancegetAuthorities()and several methods to control if the user is blocked, expired, etc.
Full class:
Notice than:
- We use
private Collection<? extends GrantedAuthority> authorities;to store the authorities (i.e. roles) in format who is understood by Spring Security. - Instead of create a constructor, we create a
builder(), who recieve aUser, extract the information from it and transform theList<Role>into authorities, and then, the builder call to constructor.
Instead of create a User and Role Service, we will create a UserDetailSeriveImpl (who implements Spring's UserDetailService), in order to recover a User from repository, and then returns a UserDetailImpl, as follows:
2.5. Payloads DTO's
In this section we are going to see the necessary classes who store information to:
- Signup a new User, to receive information from client and store a new user. This class is
SignupRequest - Signin a User, to login in our system. This class is
LoginRequest - JwtResponse, related as response to the signin request. This response will contain a JWT Token, used to Authorize subsequent request. This class is
JwtResponse
2.6. SignupRequest
This class contains information who register a new user. Is contains validation annotations.
| Java | |
|---|---|
To send this information, the json object will be something like:
| JSON | |
|---|---|
2.7. LoginRequest
Very easy class:
| Java | |
|---|---|
and the json object is something like:
Note
In this class we could mark fields as requireds, with annotation @NotBlank from javax.validation.constraints.NotBlank. You must add:
2.8. JwtResponse
This DTO class is the class that is returned as a login request. It should contain little information about our user and must important think, a JWT token. This token will be used to authorize us, as we will study in next section.
| Java | |
|---|---|
Be notice than:
- Fields on this clase will be populated from
Userclass, like a DTO. - We have changed role format, from
Roleclass toString, with a better management in clients. - The
String tokenis where the JWT token is stored. In fact a token is a String, as we will show now.
3. Tokens JWT
3.1. What is a token?
JSON Web Tokens (JWT) have been introduced as a method of secure communication between two parties. It was introduced with the RFC 7519 specification by the Internet Engineering Task Force (IETF).
Although we can use JWT with any kind of communication method, nowadays, JWT is very popular for handling authentication and authorization over HTTP.
First, you'll need to know some features of HTTP:
- HTTP is a stateless protocol, which means that an HTTP request does not maintain state. The server is not aware of any previous requests sent by the same client.
- HTTP requests should be standalone. They must include information about previous requests that the user made in the same request.
There are a few ways to do this, but the most popular way is to set a session_id, which is a reference to the user's information:
- The server will store this session ID in memory or in a database. The client will send each request with this session ID.
- The server can then obtain information about the client using this reference.
- Normally, this session ID is sent to the user as a cookie.
Here is the diagram of how session-based authentication works.
On the other hand, with JWT, when the client sends an authentication request to the server, it will send a token (token) JSON to the client, which includes all the information about the user along with the response.
The client will submit this token with all subsequent requests. Therefore, the server will not have to store any information about the session. But there is a problem with this approach. Anyone can send a fake request with a fake JSON token and pretend to be someone they're not.
For example, let's say that after authentication, the server returns a JSON object to the client with the username and expiration time. So, since the JSON object is readable, anyone can edit this information and submit a request for it. The problem is that there is no way to validate this request.
This is where the witness signature comes in. So instead of just sending a normal JSON token, the server will send a signed token, which can verify that the information doesn't change.
3.2. Structure of JWT
Let's talk about the structure of a JWT through a sample token:
As you can see, there are three sections to this JWT, each separated by a dot.
Note
Base64 encoding is a way to ensure that the data is not corrupted, as they do not compress or encrypt the data, but simply encode it in a way that most systems can understand. You can read any Base64 encoded text simply by decoding them.
First section of the JWT is a header, which is a Base64-encoded string. If you decode the header, it would look something like this:
The header section contains the hash algorithm, which was used to generate the token's sign and type.
The second section is the payload containing the JSON object that was sent back to the user. Since it is only Base64 encoded, anyone can easily decode it. It is mandatory not to include sensitive data in JWTs, such as passwords or personally identifiable information.
Typically, the body of the JWT will look something like this, although it doesn't necessarily apply:
Note
Most of the time, the sub property will contain the user ID, the iat (issued at) property, abbreviated as issued at, is the token's issuance timestamp. You may also see some common properties, such as eat or exp, which is the token's expiration time.
All these properties are the claims of the token, the information.
The final section is the token signature. This is generated by hashing the string created with previous two sections and a secret password, using the algorithm mentioned in the header section.
You can visit https://www.javainuse.com/jwtgenerator and https://jwt.io to generate tokens and test with several data, secrets and hashes.
3.3. JWT Library class
We must update our pom.xml with dependencies:
This class is who generate and checks token integrity. Let's go to show this class and what elements it needs. This class will be a library class with methods to create token, validate it and extract information from it. We will find this class with names like JWTUtils or JWTTokenProvider. This class skeleton is like follows:
- Loading or definition of token constants
- Method to generate JWT from a
Authenticationobject - Methods to get information from the token
- Method to validate token (signature)
| Java | |
|---|---|
3.4. Token generation
Let's go to see each method. Firt, we find an Authentication object. We need to now that this object represents another token (not our JWT) with the credentials of the object we want to identify.
We see that:
- We recieve an
Authenticationobjects, whom we have saved aUserDetailsImplinside it. As all user details implementations, we could get username, and we use it to set the token subject. - We set IAT tho the current time stamp.
- We establish the expiration time.
- We set the cipher algorithm and the secret word, and the token is ready to...
- the compact() method creates and transform the token to String
Note
The jwtSecret is the password we nedd. It is a good idea to store it inside application.properties and recover it to a variable, as jwtExpiration. We could use @Value annotation:
| Java | |
|---|---|
3.5. Token validation
Very easy method:
In this method we check if it is a valida token. We use parseClaimsJws(String token), after assign secret password to get the claims. If exists any problem with the token integrity, could appear a Exception. We get the claims inside a try-catch block and inform if something happens. We will return true if no Exception was catched.
Note
Remember that the claims are the content of the token payload. We do not need in this method the claims, only check if all is good
4. Authentication Controller
Now we also know how to create tokens and the User structure in our database, is time to expose our path in order to register and login users. And so, only two methods are mandatory. The class could be something like:
| Java | |
|---|---|
@CrossOriginAnnotation for permitting cross-origin requests on specific handler classes and/or handler methods. Processed if an appropriate HandlerMapping is configured. Cross-Origin Resource Sharing (CORS) is a security concept that allows restricting the resources implemented in web browsers. It prevents the JavaScript code producing or consuming the requests against different origin. For example, your web application is running on 8080 port and by using JavaScript you are trying to consume RESTful web services from 9090 port. Under such situations, you will face the Cross-Origin Resource Sharing security issue on your web browsers.@RequestMapping("/api/auth")tells that all controllers are inside/api/authpath.
Let's go to see them, but before studying methods, this class has these required variables:
@Autowired AuthenticationManager authenticationManager;→ used to create anAuthenticationtoken, used by Spring security context and by token generator.@Autowired UserRepository userRepository;→ used to access and save users.@Autowired RoleRepository roleRepository;→ to check if the roles that come in the request are valids.@Autowired PasswordEncoder encoder;→ used to encrypt user password@Autowired JwtUtils jwtUtils;→ used to create JWT tokens.
Now that we have presented the actors, let's go to the function
4.1. Signup (new user)
The method in charge to create new users, will receive a SignupRequest with this data:
| JSON | |
|---|---|
the method body is following, and let we see by blocks:
In this first part:
- We check if any user exists with same username or email, asking our repository. If any error appears, we will return a
ResponseEntityas a bad request with a description message. - Finally, we create a ner User with username, email and an encrypted password.
Next block is responsible to get the roles (stored in a String's JSONArray) and transform to a Set<Role>.
Let's go to see:
- First we check if the role set is empty. If true, we set a new
RolewithERole.ROLE_USERby default. - Otherwise, we must loop over all roles we get, checking each role in database and creating as appropiate
Roleobject.
Finally, we set the Set<Role> to the user created in first block, and in the third block we only need to store new user with UserRepositoy and send an ok response to client.
| Java | |
|---|---|
4.2. Signin (register or signin)
Note
In spanish:
- Signin: registrarse, acceder al login.
- Signup: inscribirse, darse de alta.
This controller is used to create login a user in our app. As we have studied, the DTO that we recieve in the HTTP_POST is LoginRequest class, like this:
And the method in charge is:
Take into account:
@Validannotation check that json object match withLoginRequestclass (take a look to this attribute class annotations).- We create an
Authenticationtoken (this is not the JWT Token !!!) with username and password recieved. Password is not encripted yet. - Last
Authenticationtoken is setted to theSecurityContextHolder, and object that contains a minimum security in a thread level. In this step is when Spring security subsystem works, askingUserDetailServicefor a user with this username and password in our database, and it is stored in aUserDetailsBean in memory. If any credential is wrong, it will throw an Exception, that itwill be managed by our system (we will study this later). - With this token (authentication) we generate our token. Remember that inside
jwtUtils, we only get username in order to generate our token subject. - We get the
UserDetailsobject withgetPrincipal()method, and - Transform the List of authorities into a List of String (have you studied how to map lists? And filter? And reduce?)
- Finally, we create and return a
ResponseEntity, with http_Status ok, with aJwtResponseinside it. Token plus attributes. A sample of JWTrespone is:
Note
Last token has extra newlines in order to avoid exit out of paper width, is one full String
if the username is not found on database the following response is created by our system:
| JSON | |
|---|---|
5. Tokens working
Now that user registration and login are implemented, let's ask a question, What do the client application does with the JWTResponse he has received afetr login process? User data is normally used to interface (full name, avatar, etc.). But what happens with tokens?
The answer, as we will study later is store it, and then send to the server in each request as Authorization and Authentication method.
5.1. Sending tokens
To send a token, we need to attach it in the Header section, creating an Authorization parameter with value Bearer token_recieved, as you can see on this Postman screenshot:
Important
Remember: word Bearer plus white space plus the whole token recieved (as String)
Ok thus the client application will send us the token through the request, but, how does our server to get and check the token. The answer is than we have to say it in a filter way.
6. Security Config
The class that configure security is WebSecurityConfig. Let's go to explain it by blocks again. This class is composed by a set of Beans who will be used in all project (remember code injection). We will explain only necessary.
| Java | |
|---|---|
This @Configuration tells Spring to load this class. We say that Spring allow to pre and post filters annotations (let's study later)
| Java | |
|---|---|
These beans will be explained later. In a few words are responsible for manage errors as Exception handler and how to apply filter chains to authenticate users.
| Java | |
|---|---|
This Bean use the UserDetailService and PasswordEncoder to do the authentication process. This mean access the database and check user and password (with the same encoder we use to store users). Next Beans create AuthenticationManager and encoder.
| Java | |
|---|---|
And finally one of the most important (and hard to understand) configuration, the security filter chain. Filter chain are code that we put in the middle between client and server. These filters intercepts the request, analyze it and then, dependeding the result of the filter, simple pass the control to the server or send a response to the client. This filter could change the request, adding or removing information that will be used by the server.
Take notice that we:
- Enable CORS (Cross-Origin Requests) and disble CSRF (Cross Site Request Forgery)
- Set the authenticationEntryPoint
- Allow access to all paths in
/api/auth/** - Allow access to all paths in
/api/auth/** - Any onther request will need to be authenticated
Finaly we add the before filter and the authenticationprovider.
In a relaxed view, to understand it, filters are combined with anotations that where and when we have to check Authentication and Authorization.
6.1. AuthTokenFilter
This class contains the process of the token received from clients. It must implement OncePerRequestFilter and override doFilterInternal().
In our word this method will be executed when a request is done. and:
- Extract token from the request, returning only relevant information (removes
Bearer). - The check that the token is valid, and:
- Extract the username (is in the token's payload) and
- Get that
UserDetailsand create aUsernamePasswordAuthenticationTokento be injected throw rest of the request-response, obviously with authorities. - Finally, it continues with next filter, if exists, calling
filterChain.doFilter(req,res). If not filter were, then the dispatcher pass control to the controller requested.
6.2. AuthEntryPoint
This class contains code that will be executed when an Exception appears. Then it creates a generic body into the response and create to set an accurate answer to client.
7. Controller Authorization
We only rest to say what request need to be Authorized. Remember that with other classes we prepare the Authentication, Who are you?. Now we need to ask What can you do?
As we mark in our filterChain we addFilterBefore, to check roles. But, where must we say the roles who catch each request? The answer is easy: the controllers. Let's take a look a test controller with Authorization filter:
- No annotation appears: all roles can ask for this request
@PreAuthorize("hasRole('role')")→ This annotation tells the role who is authorized to get this request. You can combine more than one role withor
8. Exercise. How to use this project?
You probably have a API rest unsecured. Now you have the task to merge your API Rest with this secure project, in order to secure and authorize only registered users. It's a hard work, but the results will be very grateful.