JWT Token In Spring Security#
Spring Security Project Configuration#
- In this session we will try to apply JWT into our spring security application for both
Authentication
andAuthorization
through custom filters. In real projects, there are many other ways for handling JWT in spring security and we will try it for next sessions. - Before implementing the JWT token in our spring security application, we need to import some dependencies and add configurations in following sections.
Dependencies#
- We will need to import dependencies below in the
pom.xml
for JWT token impelementation.
pom.xml | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
Http Security Configuration#
- As we have already known about the Session token that spring security will generated automatically by default in Spring Security Basic. So with this default behavior, a user will be authenticated for the first time he sends the request and received a session token (JSESSIONID) in the response cookies. Then for next times, the user just need to send the request with the JSESSIONID then he will be authenticated automatically without checking credentials.
- Our scope is applying JWT token into our spring security application so we need to disable this default generating JSESSIONID. So we need to add some configurations as below into the
ProjectSecurityConfig
class.
ProjectSecurityConfig.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
|
- When you look into the configuration class above, you will see we will disable generating JSESSIONID automatically by setting
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
in whichSessionCreationPolicy.STATELESS
means Spring Security will never create a HttpSession and it will never use it to obtain the SecurityContext. That means when we generate the jwt token we will handle and mange it by ourself. - Then in the CORS configuration we will add
corsConfiguration.setExposedHeaders(Collections.singletonList("Authorization"));
to expose the response headerAuthorization
for client. Because after generating a JWT token we will set it into this response header.
Create Filters For Jwt Tokens#
- Now, we will create two filters in which one filter is used for creating jwt Token and the other is used for validate the jwt token.
- Before creating filters we will need to create contain class as below:
SecurityConstant.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Filter For Creating Jwt Token#
- We will create a filter name
JwtTokenGeneratorFilter
which will extend theOncePerRequestFilter
abstract class because we want to make the token is just created only one time when our application receive a request which has not authenticated yet. The implementation code will look like below.
JwtTokenGeneratorFilter.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
- So as you can see in the
doFilterInternal()
, we will check theSecurityContext
that contains theAuthentication
or not. IfSecurityContext
had contained theAuthentication
so it means the user had been logged in successfully and we will generate the Jwt token and put it in the response's header for the user. Then we will use theJwts.builder()
to build the Jwt Token, we also need a Secret key which is a random string that we put in theapplication.yml
.
application.yml | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
- Then in the Jwt Body token we will put some information like:
Issuer
,Subject
,username
claim,authorities
claim,IssuedAt
andExpiration
.
Field | Set Method | Value |
---|---|---|
iss | setIssuer() | hardcoded |
sub | setSubject() | hardcoded |
username | claim() | value is got from Authentication |
authorities | claim() | value is got from Authentication |
iat | setIssuedAt() | currentDate |
exp | setExpiration() | currentDate + 30s |
- The life time of a jwt token will based on the
issuedAt
andexpiration
. In which, theissueAt
is the time that the jwt is created andexpiration
is the time that the jwt will be expired and can't use anymore. In this example the jwt token life time will be 30s after it is created.
Filter For Validating Jwt Token#
- We will also create a filter name
JwtTokenValidatorFilter
which will extend theOncePerRequestFilter
abstract class because we want to make the token is just validated only one time when our application receive a request. The implementation code will look like below.
JwtTokenValidatorFilter.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
- So as you can see in the
doFilterInternal()
, we will try to get theAuthorization
header in the request, then we will check the content of this header is theBearer
token or not, if it is theBearer
token so we will extract the jwt token. - After we got the jwt token, we will rebuild the Secret key that we used before in
JwtTokenGeneratorFilter
. We will parse the jwt with the key and get the body by the methodparseClaimsJws(<jwt token>).getBody()
. So in theparseClaimsJws
the signature will be checked to make the jwt token is not modified after it is created and transferred from client to our spring security application. If there is no anySignatureException()
had thrown, so the jwt is valid and we can get parameters fromClaims
likeusername
andauthorities
that we set before inJwtTokenGeneratorFilter
. - Finally, from information like
username
andauthorities
, we will create anAuthentication
and set it into theSecurityContext
. - Moreover, you can also see in
JwtTokenValidatorFilter
, we will also implement the methodshouldNotFilter()
for the api/v1/user
which is used for login user. It means that any request come into this api will be ignored byJwtTokenValidatorFilter
because we want the user should be able to login into our application.
Add Jwt Filters Into Filter Chain#
- After creating 2 filters:
JwtTokenGeneratorFilter
andJwtTokenValidatorFilter
, we will add theJwtTokenGeneratorFilter
after theBasicAuthenticationFilter
because we want to make sure users had to log in successfully before we create jwt token for them in the responses. Then the filterJwtTokenValidatorFilter
will be added beforeBasicAuthenticationFilter
because we want to make any request with containsAuthorization
header with jwt token will be validated and used for authentication and authorization.
ProjectSecurityConfig.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
|
Testing#
- So now, let's start our spring security application and call the api
/v1/user
to login and get the jwt token in the response header.
- Then, let's copy this jwt token and add it into the
Authorization
request withBearer Token
type of another api/v1/loan
and call it. You should see the response 200 as below.
- Then if you look into the response header of
/v1/load
, you can also see there is another jwt response and you can also use this jwt token to call to our applications.
- Now, if you do nothing and wait for more than 30s, then call the api again, you should received an 500 error because the jwt token had been expired.