Spring Cloud Security - OAuth2 Authorization using JWT (JSON Web Token)
[ Spring Cloud
Spring Cloud Security
] Oauth2 is a widely used authorization framework and is supported by Spring. The Spring OAuth 2.0 Authorization mechanism manages and verifies the OAuth 2.0 tokens. These tokens are then used to access the protected resources.
Without going much into theory, let’s assume a real world security problem statement and see how we can accomplish our desired solution using JWT (JSON Web Token) OAuth2 security features provided by Spring.
Table of contents
- Problem Statement
- Build Spring Boot OAuth2 Authorization Service
- Test Authorization Service
- Build Car Inventory Service
- Test Car Inventory Service
- Check Token
- Refresh Token
Problem Statement
Let’s say we have a Car Inventory Service which has two webservice methods - viewCars()
and addCars()
. We are entrusted to protect our service by giving access to users john and kelly where john can invoke both viewCars()
and addCars()
where as kelly can invoke viewCars()
.
The above scenario is a typical authentication and authorization scenario where we are supposed to authenticate the user and check if the authenticated user is authorized to access the resources/services.
Keeping this problem statement in mind, let’s build 2 services:
-
Spring Boot OAuth2 Authorization Service: This will be our authorization server. We will be using this service to get the user’s access token. This token will then be used while invoking webservice methods.
-
Car Inventory Service: This will be our service which needs to be protected by giving access to only those who need them. In Spring terminology, this is called as a Resource Server. Note that this servce will use
Spring Boot OAuth2 Authorization Service
to authenticate the token passed by the user.
Build Spring Boot OAuth2 Authorization Service
Building the bare bone Spring Boot Service is simple when Spring Initializr
is used. Spring Initializr
generates spring boot project with just what you need to start quickly! Let’s start off with one.
Create a Spring Boot starter project using Spring Initializr
Let’s utilize the pre-configured Spring Initializr
which is available here to create jwt-oauth2-authorization-server starter project.
Click on Generate Project. This downloads a zip file containing jwt-oauth2-authorization-server
project. Import the project to your IDE.
JWT dependency
pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
Create tables for users, groups, group authorities and group members
For Spring OAuth2 mechanism to work, we need to create tables to hold users, groups, group authorities and group members. We can create these tables as part of application start up by providing the table definations in schema.sql
file as shown below. This setup is good enough for POC code.
Note that these tables will be created in the embedded H2 database when the oauth2-authorization-server
app starts and will be dropped when you shut down the app. This setup is good enough for POC purposes.
src/main/resources/schema.sql
drop table users if exists;
create table users(
username varchar_ignorecase(256) not null primary key,
password varchar_ignorecase(256) not null,
enabled boolean not null
);
drop table groups if exists;
create table groups (
id bigint generated by default as identity(start with 0) primary key,
group_name varchar_ignorecase(50) not null
);
drop table group_authorities if exists;
create table group_authorities (
group_id bigint not null,
authority varchar(50) not null,
constraint fk_group_authorities_group foreign key(group_id) references groups(id)
);
drop table group_members if exists;
create table group_members (
id bigint generated by default as identity(start with 0) primary key,
username varchar(50) not null,
group_id bigint not null,
constraint fk_group_members_group foreign key(group_id) references groups(id)
);
Add users, groups, group authorities and group members
Now that we have the tables ready, let’s create DMLs to store john and kelly’s user credentails, their access details etc. We can provide these DMLs in data.sql
so that spring will execute these DMLs when the oauth2-authorization-server
service is started.
-
Let’s create users named
john
with a passwordjohn@123
andkelly
with a passwordkelly@123
. -
Create a group
INVENTORY_GROUP_1
and assign the rolesINVENTORY_VIEW
andINVENTORY_ADD
and addjohn
to this group. -
Similarly create a group
INVENTORY_GROUP_2
with roleINVENTORY_VIEW
and addkohn
to this group.
Spring OAuth2 Security framework allows us to choose one of many available password encoders to store the password. For this tutorial, we shall use BCrypt Password Encoder. I have used CodeachesBCryptPasswordEncoder.java
available here to get the Bcrypt encrypted passwords.
john's Bcrypt encrypted password = $2a$04$xqJH/AWpC89pBBFb7i9VU.zoWbOrE2gvdFcfTAOE1bCF5.tNvVXXu
john's Bcrypt encrypted password = $2a$04$IpZnGqXXgNvvMbqlg/tc7uJUM.1nj/5KtqnFlxRpRN2RqWUFV4lg6
-
Here,
$2a$04$xqJH/AWpC89pBBFb7i9VU.zoWbOrE2gvdFcfTAOE1bCF5.tNvVXXu
is Bcrypt encrypted john’s password. -
And
$2a$04$IpZnGqXXgNvvMbqlg/tc7uJUM.1nj/5KtqnFlxRpRN2RqWUFV4lg6
is Bcrypt encrypted kelly’s password.
src/main/resources/data.sql
--
--Users: john/john@123 kelly/kelly@123
--Password encrypted using CodeachesBCryptPasswordEncoder.java (4 rounds)
--
INSERT INTO users (username,password,enabled)
VALUES ('john', '$2a$04$xqJH/AWpC89pBBFb7i9VU.zoWbOrE2gvdFcfTAOE1bCF5.tNvVXXu', TRUE);
INSERT INTO users (username,password,enabled)
VALUES ('kelly','$2a$04$IpZnGqXXgNvvMbqlg/tc7uJUM.1nj/5KtqnFlxRpRN2RqWUFV4lg6', TRUE);
INSERT INTO groups (id, group_name) VALUES (1, 'INVENTORY_GROUP_1');
INSERT INTO groups (id, group_name) VALUES (2, 'INVENTORY_GROUP_2');
INSERT INTO group_authorities (group_id, authority) VALUES (1, 'INVENTORY_VIEW');
INSERT INTO group_authorities (group_id, authority) VALUES (1, 'INVENTORY_ADD');
INSERT INTO group_authorities (group_id, authority) VALUES (2, 'INVENTORY_VIEW');
INSERT INTO group_members (username, group_id) VALUES ('john', 1);
INSERT INTO group_members (username, group_id) VALUES ('kelly', 2);
Create tables for clients, users and groups
Now, let’s create tables to hold the user credentials, user access details for each of the service/resource. These are standard DDLs provided by Spring. We shall add the below DDLs to our existing schema.sql
file.
src/main/resources/schema.sql
drop table oauth_client_details if exists;
create table oauth_client_details (
client_id varchar(256) primary key,
resource_ids varchar(256),
client_secret varchar(256),
scope varchar(256),
authorized_grant_types varchar(256),
web_server_redirect_uri varchar(256),
authorities varchar(256),
access_token_validity integer,
refresh_token_validity integer,
additional_information varchar(4096),
autoapprove varchar(256)
);
drop table oauth_access_token if exists;
create table oauth_access_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication LONGVARBINARY,
refresh_token VARCHAR(256)
);
drop table oauth_refresh_token if exists;
create table oauth_refresh_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication LONGVARBINARY
);
oauth_client_details
table is used to store client details.oauth_access_token
andoauth_refresh_token
is used internally by OAuth2 server to store the user tokens.
Create a client
Let’s insert a record in oauth_client_details
table for a client named appclient
with a password appclient@123
.
-
Here,
appclient
is the ID has access to thecarInventory
resource. -
I have used
CodeachesBCryptPasswordEncoder.java
available here to get the Bcrypt encrypted password.
appclient's Bcrypt encrypted password = $2a$04$ZVENvHhtvDKPSgMsP9AK0usr9o3Dpo2G3aSAT1HQZSZUB7CoAP6QC
src/main/resources/data.sql
--
--Client: appclient/appclient@123
--Password encrypted using CodeachesBCryptPasswordEncoder.java (4 rounds)
--
INSERT INTO
oauth_client_details (
client_id,
client_secret,
resource_ids,
scope,
authorized_grant_types,
access_token_validity,
refresh_token_validity
)
VALUES
(
'appclient',
'$2a$04$ZVENvHhtvDKPSgMsP9AK0usr9o3Dpo2G3aSAT1HQZSZUB7CoAP6QC',
'carInventory',
'read,write',
'authorization_code,check_token,refresh_token,password',
1000000,
1000000
);
Enable OAuth2 mechanism
Annotate the JwtOauth2AuthorizationServerApplication.java
with @EnableAuthorizationServer
. This enables the Spring to consider this service as authorization Server.
com.codeaches.oauth2.jwt.authorization.server.JwtOauth2AuthorizationServerApplication.java
@EnableAuthorizationServer
@SpringBootApplication
public class JwtOauth2AuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(JwtOauth2AuthorizationServerApplication.class, args);
}
}
Configure OAuth2 Server
Let’s create a class AuthServerConfig.java
with below details.
- JwtAccessTokenConverter is a helper class that translates between JWT encoded token values and OAuth authentication information (in both directions). It also acts as a TokenEnhancer when tokens are granted.
- BCryptPasswordEncoder implements PasswordEncoder that uses the BCrypt strong hashing function. Clients can optionally supply a “strength” (a.k.a. log rounds in BCrypt) and a SecureRandom instance. The larger the strength parameter the more work will have to be done (exponentially) to hash the passwords. The value used in this example is 8 for client secret.
- AuthorizationServerEndpointsConfigurer configures the non-security features of the Authorization Server endpoints, like token store, token customizations, user approvals and grant types.
- AuthorizationServerSecurityConfigurer configures the security of the Authorization Server, which means in practical terms the
/oauth/token
endpoint. - ClientDetailsServiceConfigurer configures the ClientDetailsService, e.g. declaring individual clients and their properties.
com.codeaches.oauth2.jwt.authorization.server.AuthServerConfig.java
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
DataSource ds;
@Autowired
AuthenticationManager authMgr;
@Autowired
private UserDetailsService usrSvc;
@Bean("clientPasswordEncoder")
PasswordEncoder clientPasswordEncoder() {
return new BCryptPasswordEncoder(8);
}
@Bean
JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("JWTKey@123");
return converter;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer cfg) throws Exception {
// Enable /oauth/token_key URL used by resource server to validate JWT tokens
cfg.tokenKeyAccess("permitAll");
// Enable /oauth/check_token URL
cfg.checkTokenAccess("permitAll");
// BCryptPasswordEncoder(8) is used for oauth_client_details.user_secret
cfg.passwordEncoder(clientPasswordEncoder());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(ds);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(jwtAccessTokenConverter());
endpoints.authenticationManager(authMgr);
endpoints.userDetailsService(usrSvc);
}
}
Configure User Security Authentication
Let’s create a class UserSecurityConfig.java
to handle user authentication.
com.codeaches.oauth2.jwt.authorization.server.UserSecurityConfig.java
- setEnableAuthorities(false) disables the usage of authorities table and setEnableGroups(true) enables the usage of groups, group authorities and group members tables.
- BCryptPasswordEncoder implements PasswordEncoder that uses the BCrypt strong hashing function. Clients can optionally supply a “strength” (a.k.a. log rounds in BCrypt) and a SecureRandom instance. The larger the strength parameter the more work will have to be done (exponentially) to hash the passwords. The value used in this example is 4 for user’s password
@Configuration
public class UserSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DataSource ds;
@Override
@Bean(BeanIds.USER_DETAILS_SERVICE)
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
@Override
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean("userPasswordEncoder")
PasswordEncoder userPasswordEncoder() {
return new BCryptPasswordEncoder(4);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// BCryptPasswordEncoder(4) is used for users.password column
JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> cfg = auth.jdbcAuthentication()
.passwordEncoder(userPasswordEncoder()).dataSource(ds);
cfg.getUserDetailsService().setEnableGroups(true);
cfg.getUserDetailsService().setEnableAuthorities(false);
}
}
Spring Boot Startup Console
Start the Spring Boot OAuth2 Authorization Service application. It will run on the default port 8080
.
Exposing 2 endpoint(s) beneath base path '/actuator'
Tomcat started on port(s): 8080 (http) with context path ''
Started JwtOauth2AuthorizationServerApplication in 6.998 seconds (JVM running for 8.117)
Test Authorization Service
Get Access Token
Let’s get the access token for john by passing his credentials as part of header along with authorization details of appclient
. Authorization details of appclient
is derived by encoding appclient:appclient@123
in Base64 format. In our example, the value is YXBwY2xpZW50OmFwcGNsaWVudEAxMjM=
I have used CodeachesBCryptPasswordEncoder.java
available here to get the Base64 encrypted value of appclient:appclient@123
.
HTTP POST Request
curl --request POST http://localhost:8080/oauth/token \
--header "Authorization:Basic YXBwY2xpZW50OmFwcGNsaWVudEAxMjM=" \
--data "grant_type=password" \
--data "username=john" \
--data "password=john@123"
HTTP POST Response
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiMGYxNWI0NWMtYjE1MS00MDgzLTg1Y2MtYjc3MTNhNGEwYWU5IiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.n8fZwyUEpi1yfzBK8SITQSI7Hfz8-icwzOx_TA0w7k8",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiMGYxNWI0NWMtYjE1MS00MDgzLTg1Y2MtYjc3MTNhNGEwYWU5IiwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiNmJjYzQ3ZWItYTZjYy00MTI3LWJjOWUtZWI5M2Q2YWMwZWZkIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.X7oJTgwJqLcATXbcf-i7MBMj-8fSrVDqONr-tNTZKmk",
"expires_in":999999,
"scope":"read write",
"jti":"0f15b45c-b151-4083-85cc-b7713a4a0ae9"
}
HTTP POST Request
On similar lines, let’s get the access token for kelly.
curl --request POST http://localhost:8080/oauth/token \
--header "Authorization:Basic YXBwY2xpZW50OmFwcGNsaWVudEAxMjM=" \
--data "grant_type=password" \
--data "username=kelly" \
--data "password=kelly@123"
HTTP POST Response
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiZjQ4MjA3M2EtNzQxNy00MTc5LTgyYmItZjg1N2Y1Yjc1NmUwIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.mNKpIIGDlbwb_r62UmyLG6hnwkGGitYljDHU0N-5nP0",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImY0ODIwNzNhLTc0MTctNDE3OS04MmJiLWY4NTdmNWI3NTZlMCIsImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiOGZkZTg0NzktMzhjZC00Yzk5LTlkNGMtOTZiYThmOGUyZjIyIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.JIvQtWsxPKhcU061oNnzPeCpEiek6T5f7zAP-OemnoE",
"expires_in":1000000,
"scope":"read write",
"jti":"f482073a-7417-4179-82bb-f857f5b756e0"
}
This completes the building of our Spring Boot OAuth2 Authorization service. Let’s move on to building our Car Inventory Resource Service.
Build Car Inventory Service
Let’s utilize the pre-configured Spring Initializr
which is available here to create jwt-car-inventory-service starter project.
Click on Generate Project. This downloads a zip file containing jwt-car-inventory-service
project. Import the project to your IDE.
Enable Resource Server mechanism
The first step is to annotate the JwtCarInventoryServiceApplication.java
with @EnableResourceServer
. This enables the Spring to authenticate requests via an incoming OAuth2 token.
com.codeaches.carinventoryservice.JwtCarInventoryServiceApplication.java
@SpringBootApplication
@EnableResourceServer
public class JwtCarInventoryServiceApplication {
public static void main(String[] args) {
SpringApplication.run(JwtCarInventoryServiceApplication.class, args);
}
}
Rest Controller
Let’s create a class InventoryController.java
and configure REST methods viewCars()
and addCarss()
.
/viewCars
can be acessed by user who belongs toINVENTORY_VIEW
. In our case, that would be both john and kelly./addCarss
can be acessed by user who belongs toINVENTORY_ADD
. In our case, that would be only john.
com.codeaches.carinventoryservice.InventoryController.java
@RestController
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class InventoryController {
@GetMapping("viewCars")
@PreAuthorize("hasAuthority('INVENTORY_VIEW')")
public Set<String> viewCars() {
return cars;
}
@PostMapping("addCars")
@PreAuthorize("hasAuthority('INVENTORY_ADD')")
public Set<String> addCars(@RequestBody HashMap<String, String> payload) {
cars.addAll(payload.values());
return cars;
}
static Set<String> cars = new HashSet<>();
static {
cars.add("Toyota");
cars.add("Benz");
}
}
Configure Car Inventory Service with OAuth2 Service URI and Client Credentials
- Update Car Inventory Service with client credentials for
appclient
and the/oauth/check_token
URL of OAuth2 Authorization Server. - Here the client
appclient
is authorized to access Car Inventory resource. We had configured this inoauth_client_details
table. - Update the server port to run on
9090
. - Add JWT signing key
JWTKey@123
. - Note that the value of
security.oauth2.resource.jwt.key-value
should match the Signing Key provided in the OAuth2 Server. Please refer toAuthServerConfig.java
in OAuth2 server. - Instead of
security.oauth2.resource.jwt.key-value
, we can also configuresecurity.oauth2.resource.jwt.key-uri
withhttp://localhost:8080/oauth/token_key
for token validation.
src/main/resources/application.properties
server.port=9090
security.oauth2.client.client-id=appclient
security.oauth2.client.client-secret=appclient@123
security.oauth2.resource.id=carInventory
security.oauth2.resource.jwt.key-value=JWTKey@123
#security.oauth2.resource.jwt.key-uri=http://localhost:8080/oauth/token_key
Spring Boot Startup Console
Start the Car Inventory Service application. It will run on port 9090
.
Completed initialization in 8 ms
Tomcat started on port(s): 9090 (http) with context path ''
Started JwtCarInventoryServiceApplication in 5.025 seconds (JVM running for 6.112)
Test Car Inventory Service
Now that both the services are up and running, let’s test the viewCars
webservice method by passing the earlier access token obtained for user john in the header as shown below. Use the earlier obtained access_token
for john in the request header Authorization:Bearer <access_token>
.
Here john belongs to INVENTORY_VIEW
and hence he can view the cars.
HTTP GET Request
curl --request GET http://localhost:9090/viewCars \
--header "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiMGYxNWI0NWMtYjE1MS00MDgzLTg1Y2MtYjc3MTNhNGEwYWU5IiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.n8fZwyUEpi1yfzBK8SITQSI7Hfz8-icwzOx_TA0w7k8"
HTTP GET Response
["Benz","Toyota"]
Now let’s test the addCars
webservice method for the user john.
Here john belongs to INVENTORY_ADD
and hence he can add cars to inventory.
HTTP POST Request
curl --request POST http://localhost:9090/addCars \
--header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiMGYxNWI0NWMtYjE1MS00MDgzLTg1Y2MtYjc3MTNhNGEwYWU5IiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.n8fZwyUEpi1yfzBK8SITQSI7Hfz8-icwzOx_TA0w7k8" \
--header "Content-Type: application/json" \
--data '{"car":"BMW"}'
HTTP POST Response
["Benz","Toyota","BMW"]
HTTP GET Request
On similar lines, let’s test the viewCars
webservice method by passing the earlier access token obtained for user kelly in the header as shown below. Use the earlier obtained access_token
for kelly in the request header Authorization:Bearer <access_token>
.
curl --request GET http://localhost:9090/viewCars \
--header "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiZjQ4MjA3M2EtNzQxNy00MTc5LTgyYmItZjg1N2Y1Yjc1NmUwIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.mNKpIIGDlbwb_r62UmyLG6hnwkGGitYljDHU0N-5nP0"
HTTP GET Response
["Benz","Toyota","BMW"]
HTTP POST Request
Now let’s test the addCars
webservice method for the user kelly.
Unfortunately kelly does not belong to INVENTORY_ADD
and hence kelly cannot add cars to inventory.
curl --request POST http://localhost:9090/addCars \
--header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiZjQ4MjA3M2EtNzQxNy00MTc5LTgyYmItZjg1N2Y1Yjc1NmUwIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.mNKpIIGDlbwb_r62UmyLG6hnwkGGitYljDHU0N-5nP0" \
--header "Content-Type: application/json" \
--data '{"car":"Honda"}'
HTTP POST Response
{"error":"access_denied","error_description":"Access is denied"}
Check Token
check_token is used to check the token validity and access details about the user.
Below example shows the check_token
URI invocation for john and use the earlier obtained access_token of john.
HTTP POST Request
curl --request POST http://localhost:8080/oauth/check_token \
--data "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiMGYxNWI0NWMtYjE1MS00MDgzLTg1Y2MtYjc3MTNhNGEwYWU5IiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.n8fZwyUEpi1yfzBK8SITQSI7Hfz8-icwzOx_TA0w7k8"
HTTP POST Response
{
"aud":[
"carInventory"
],
"user_name":"john",
"scope":[
"read",
"write"
],
"active":true,
"exp":1578814169,
"authorities":[
"INVENTORY_ADD",
"INVENTORY_VIEW"
],
"jti":"0f15b45c-b151-4083-85cc-b7713a4a0ae9",
"client_id":"appclient"
}
HTTP POST Request
Below example shows the check_token
URI invocation for kelly and and use the earlier obtained access_token of kelly.
curl --request POST http://localhost:8080/oauth/check_token \
--data "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiZjQ4MjA3M2EtNzQxNy00MTc5LTgyYmItZjg1N2Y1Yjc1NmUwIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.mNKpIIGDlbwb_r62UmyLG6hnwkGGitYljDHU0N-5nP0"
HTTP POST Response
{
"aud":[
"carInventory"
],
"user_name":"kelly",
"scope":[
"read",
"write"
],
"active":true,
"exp":1578814257,
"authorities":[
"INVENTORY_VIEW"
],
"jti":"f482073a-7417-4179-82bb-f857f5b756e0",
"client_id":"appclient"
}
Refresh Token
refresh_token
is used to get a new token for the user.
HTTP POST Request
In the below example we are getting a new token for john by passing the earlier obtained refresh_token
for john.
curl --request POST http://localhost:8080/oauth/token \
--header "Authorization:Basic YXBwY2xpZW50OmFwcGNsaWVudEAxMjM=" \
--data "grant_type=refresh_token" \
--data "refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiMGYxNWI0NWMtYjE1MS00MDgzLTg1Y2MtYjc3MTNhNGEwYWU5IiwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiNmJjYzQ3ZWItYTZjYy00MTI3LWJjOWUtZWI5M2Q2YWMwZWZkIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.X7oJTgwJqLcATXbcf-i7MBMj-8fSrVDqONr-tNTZKmk"
HTTP POST Response
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTc4ODIwMjg0LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiY2EyYzNkZjQtYmNjYy00MTIzLWFjOTYtODI5YTMzMTc4NjRhIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.VrmN5t7tCEyfDqz1pe2qbKq-272mwR2g2C49j9t111w",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImpvaG4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiY2EyYzNkZjQtYmNjYy00MTIzLWFjOTYtODI5YTMzMTc4NjRhIiwiZXhwIjoxNTc4ODE0MTY5LCJhdXRob3JpdGllcyI6WyJJTlZFTlRPUllfQUREIiwiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiNmJjYzQ3ZWItYTZjYy00MTI3LWJjOWUtZWI5M2Q2YWMwZWZkIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.-uB4RJyNvrV1Qqo7Ebyzhf7_qqM0LfUd36Ou-8RilnM",
"expires_in":999999,
"scope":"read write",
"jti":"ca2c3df4-bccc-4123-ac96-829a3317864a"
}
HTTP POST Request
Similarly we are getting a new token for kelly by passing the earlier obtained refresh_token
for kelly.
curl --request POST http://localhost:8080/oauth/token \
--header "Authorization:Basic YXBwY2xpZW50OmFwcGNsaWVudEAxMjM=" \
--data "grant_type=refresh_token" \
--data "refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImY0ODIwNzNhLTc0MTctNDE3OS04MmJiLWY4NTdmNWI3NTZlMCIsImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiOGZkZTg0NzktMzhjZC00Yzk5LTlkNGMtOTZiYThmOGUyZjIyIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.JIvQtWsxPKhcU061oNnzPeCpEiek6T5f7zAP-OemnoE"
HTTP POST Response
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTU3ODgyMDM0MSwiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiOTIwZGQ2ODEtODgyYS00OTU2LWIzMGYtNTU3OWY1NjUzYTVmIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.6s-14SzwcxmdHBZ0O2446dv1dShz60A9IRt-BB2ENfk",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiY2FySW52ZW50b3J5Il0sInVzZXJfbmFtZSI6ImtlbGx5Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6IjkyMGRkNjgxLTg4MmEtNDk1Ni1iMzBmLTU1NzlmNTY1M2E1ZiIsImV4cCI6MTU3ODgxNDI1NywiYXV0aG9yaXRpZXMiOlsiSU5WRU5UT1JZX1ZJRVciXSwianRpIjoiOGZkZTg0NzktMzhjZC00Yzk5LTlkNGMtOTZiYThmOGUyZjIyIiwiY2xpZW50X2lkIjoiYXBwY2xpZW50In0.6Bh3dYYFiL8vllhH70mnBN_aymi1Xedm8QqUPsBQ_nY",
"expires_in":999999,
"scope":"read write",
"jti":"920dd681-882a-4956-b30f-5579f5653a5f"
}
Summary
This concludes the creation of Spring Boot OAuth2 Authorization and Resource Servers with JSON Web Token (JWT) Store.
Your feedback is always appreciated. Happy coding!
Related Posts: