Friday, November 15, 2024

How to Iterate Dictionary in Python

In this tutorial we'll see how you can iterate or loop over a dictionary in Python. Some of the options are:

  1. Iterate dictionary directly
  2. Iterate a dictionary using keys method
  3. Iterate a dictionary using values method
  4. Iterate a dictionary using items method

Apart from that we'll also see how to add or remove element from a dictionary while iterating it. See example here.

1. Iterate dictionary directly

If you want to retrieve keys from a dictionary you can use a for loop directly with a dictionary.

def dictIterator(d):
    for k in d:
        print(k)
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
#call function
dictIterator(countries)

Output

US
GB
DE
IN

Since you already retrieve keys by this way of iteration, you can also get values by passing keys to dictionary. Here is the changed code

def dictIterator(d):
    for k in d:
        print('key=', k, 'value=', d[k])
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

key= US value= United States
key= GB value= United Kingdom
key= DE value= Germany
key= IN value= India

2. Iterate a dictionary using keys method

Dictionary in Python has a keys() method which returns dictionary's keys as a view object. You can iterate over this view object using for loop.

def dictIterator(d):
    print(type(d.keys()))
    for k in d.keys():
        print('key=', k, 'value=', d[k])
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

<class 'dict_keys'>
key= US value= United States
key= GB value= United Kingdom
key= DE value= Germany
key= IN value= India

As you can see keys() method returns an object of type dict_keys.

3. Iterate a dictionary using values method

Above two ways of iterating a dictionary gives you keys of the dictionary. If you want values then there is also a values() method in dictionary which returns dictionary's values as a view object. You can iterate over this view object using for loop.

def dictIterator(d):
    print(type(d.values()))
    for v in d.values():
        print(v)
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

<class 'dict_values'&glt;
United States
United Kingdom
Germany
India

As you can see values() method returns an object of type dict_values.

4. Iterate a dictionary using items method

There is also a items() method in dictionary which returns a view object that contains both key and value.

def dictIterator(d):
    print(type(d.items()))
    for item in d.items():
        print(item)
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

<class 'dict_items'>
('US', 'United States')
('GB', 'United Kingdom')
('DE', 'Germany')
('IN', 'India')

As you can see items() method returns an object of type dict_items. Note that each item is of type tuple so you can unpack that tuple to get key and value.

def dictIterator(d):
    for k, v in d.items():
        print('Key =', k, 'Value =', v)
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

Key = US Value = United States
Key = GB Value = United Kingdom
Key = DE Value = Germany
Key = IN Value = India

5. Changing dictionary while looping

If you try to make a structural change in a dictionary while iterating it then RuntimeError is raised. Structural change here means adding or deleting items which changes the size of the dictionary while it is iterated. Updating any value doesn't come under structural change.

For example, trying to add new item while iterating.

def dictIterator(d):
    for k in d:
        print(k)
        d['JP'] = 'Japan'
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

RuntimeError: dictionary changed size during iteration

Same way trying to remove an item while iterating the dictionary results in RuntimeError.

def dictIterator(d):
    for k in d:
        print(k)
        if k == 'US':
            del d[k]
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

RuntimeError: dictionary changed size during iteration

In such a scenario where you are going to make a structural change to the dictionary while iterating it, you should make a shallow copy of the dictionary, using copy() method. Then you can iterate over that shallow copy and make changes to the original dictionary.

def dictIterator(d):
    #iterate copy of the dictionary
    for k in d.copy():
        if k == 'US':
            del d[k]
    print(d)
    
countries = {'US': 'United States', 'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}
dictIterator(countries)

Output

{'GB': 'United Kingdom', 'DE': 'Germany', 'IN': 'India'}

That's all for this topic How to Iterate Dictionary in Python. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. List in Python With Examples
  2. Tuple in Python With Examples
  3. Named Tuple in Python

You may also like-

  1. Variable Length Arguments (*args), Keyword Varargs (**kwargs) in Python
  2. Global Keyword in Python With Examples
  3. Bubble Sort Program in Python
  4. String Length in Python - len() Function
  5. Java Stream - Collectors.groupingBy() With Examples
  6. Deque Implementation in Java Using Doubly Linked List
  7. JavaScript let and const With Examples
  8. Spring Integration With Quartz Scheduler

Dictionary in Python With Examples

Dictionary in Python is a built-in data type that is used to store elements in the form of key:value pairs. Value is said to be mapped with the key and you can extract the value by passing the mapped key. Dictionaries are indexed by keys and dictionaries in Python are mutable just like list in Python.

Creating a dictionary

1. You can create dictionary by storing elements with in curly braces {}. Elements here are comma separated key:value pairs. Each key:value pair has colon ( : ) in between.

Syntax

d = {
    key1: value1,
    key2: value2,
      .
      .
      .
    keyn: valuen
}

Here are some examples of creating dictionary.

#empty dictionary
d = {}
print(d) #{}

#dictionary with int as key and string as value
d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}

# nested dictionary- Each value is another dictionary
d = {'1001':{'id':1001, 'name':'Ram', 'age': 43}, '1002':{'id':1002, 'name':'Chaya', 'age': 34}}
print(d) #{'1001': {'id': 1001, 'name': 'Ram', 'age': 43}, '1002': {'id': 1002, 'name': 'Chaya', 'age': 34}}

2. You can also create a dictionary using the dict() constructor by passing a sequence of key-value pairs.

Syntax

d = dict([
    (key1, value1),
    (key2, value2),
      .
      .
      .
    (keyn: valuen)
])

Here are some examples of creating dictionary using dict() constructor.

d = dict([(1, 'Ashish'), (2, 'Bhavya'), (3, 'Chandan'), (4, 'Dinesh')])
print(d) # {1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}

When the keys are simple strings, you can also specify pairs using keyword arguments.

d = dict(NDL='New Delhi', KOL='Kolakata', MUM='Mumbai', HYD='Hyderabad')
print(d) # {'NDL': 'New Delhi', 'KOL': 'Kolkata', 'MUM': 'Mumbai', 'HYD': 'Hyderabad'}

Python 3.7 onward, dictionaries preserve insertion order, meaning that keys will be produced in the same order they were added sequentially over the dictionary.

Retrieving value in dictionary

Once dictionary is created you can access any value by passing the mapped key.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
val = d[1]
print(val) # Ashish

As you can see by passing key 1 you get the mapped value 'Ashish'

d = dict(NDL='New Delhi', KOL='Kolkata', MUM='Mumbai', HYD='Hyderabad')
print(d['KOL']) #Kolkata

If you pass any key which is not present in the dictionary, Python raises KeyError exception.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
val = d[10]
print(val)

Output

Traceback (most recent call last):

  File d:\netjs\netjs_2017\python\pythonnewws\mydictionary.py:9
    val = d[10]

KeyError: 10

Retrieving value in nested dictionary

You can retrieve value from the nested dictionary by passing respective keys in square brackets.

For example-

d = {'1001':{'id':1001, 'name':'Ram', 'age': 43}, '1002':{'id':1002, 'name':'Chaya', 'age': 34}}

Here we have nested dictionary mapped with each key. If we want to retrieve name as 'Ram' then we first have to pass key as '1001' to get the mapped value. Since value is again a dictionary so we need to pass another key as 'name' to get the mapped value.

d['1001']['name']

Retrieving value using get method in dictionary

In the previous example we have seen that passing a key that is not present in the dictionary results in KeyError. If you want to avoid that you can use get method which has the following syntax.

get(key, default=None)

Method returns the value for key if key is in the dictionary, else default. If default value is not passed, it defaults to None, so that this method never raises a KeyError.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
val = d.get(10, 'Not Found')
print(val) #Not Found

Adding new key:value pair

Adding new key:value pair to the dictionary is done by assigning value to a respective key.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
# new key:value
d[5] = 'Esha'
print(d) # {1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh', 5: 'Esha'}

Updating value for an existing key

If you want to update value for an existing key that can be done by assigning value to that key.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
#changed value for key=3
d[3] = 'Chirag'
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chirag', 4: 'Dinesh'}

Removing element from Dictionary

A key:value pair can be removed from a dictionary using del statement.

d = dict([(1, 'Ashish'), (2, 'Bhavya'), (3, 'Chandan'), (4, 'Dinesh')])
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
del(d[2])
print(d) #{1: 'Ashish', 3: 'Chandan', 4: 'Dinesh'}

There is also a pop(key[,default]) method in dictionary which removes the key if it is in the dictionary and returns its value, else returns default. If default is not given and key is not in the dictionary, a KeyError is raised.

d = dict([(1, 'Ashish'), (2, 'Bhavya'), (3, 'Chandan'), (4, 'Dinesh')])
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
print(d.pop(2)) #Bhavya
print(d) #{1: 'Ashish', 3: 'Chandan', 4: 'Dinesh'}

Getting the count of items in a dictionary

You can get the count of items in a dictionary using len() function.

len(d)- Returns the count of items in the dictionary d.

d = dict([(1, 'Ashish'), (2, 'Bhavya'), (3, 'Chandan'), (4, 'Dinesh')])
print(len(d)) #4

Rules for keys in the dictionary

Dictionary in Python has certain rules for keys.

1. Key should be immutable. You can use any datatype as key which is immutable like strings and numbers. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key.

This restriction is there because immutable datatypes are hashable. Dictionary is a HashTable based data structure which uses key to calculate a hash value to determine where to store the mapped value. For retrieval hash value of the passed key is calculated to determine the location of the value.

In the example code one of the keys is a list.

code = ['NDL']
city = {code: 'New Delhi', 'KOL': 'Kolkata', 'MUM': 'Mumbai', 'HYD': 'Hyderabad'}
print(city)

Output

File d:\netjs\netjs_2017\python\pythonnewws\mydictionary.py:9
    city = {code: 'New Delhi', 'KOL': 'Kolkata', 'MUM': 'Mumbai', 'HYD': 'Hyderabad'}

TypeError: unhashable type: 'list'

2. Keys in a dictionary should be unique, if you pass the same key again that doesn't result in an error but overwrites the previous value.

In the example code key 3 is duplicate.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh', 3:'Chirag'}
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chirag', 4: 'Dinesh'}

Methods in Python dictionary

Some of the operations supported by dictionaries in Python.

1. clear()- Used to remove all items from the dictionary.

d = dict(NDL='New Delhi', KOL='Kolkata', MUM='Mumbai', HYD='Hyderabad')
print(d) #{'NDL': 'New Delhi', 'KOL': 'Kolkata', 'MUM': 'Mumbai', 'HYD': 'Hyderabad'}
d.clear()
print(d) #{}

2. items()- Returns a view object that contains each key:value pair as a tuple in a list.

d = dict(NDL='New Delhi', KOL='Kolkata', MUM='Mumbai', HYD='Hyderabad')
print(d.items()) #dict_items([('NDL', 'New Delhi'), ('KOL', 'Kolkata'), ('MUM', 'Mumbai'), ('HYD', 'Hyderabad')])

3. keys()- Returns a view object of the dictionary's keys.

d = dict(NDL='New Delhi', KOL='Kolkata', MUM='Mumbai', HYD='Hyderabad')
print(d.keys()) # dict_keys(['NDL', 'KOL', 'MUM', 'HYD'])

4. pop()- Removes an element from the dictionary for the passed key and returns the value.

d = dict([(1, 'Ashish'), (2, 'Bhavya'), (3, 'Chandan'), (4, 'Dinesh')])
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
print(d.pop(2)) #Bhavya
print(d) #{1: 'Ashish', 3: 'Chandan', 4: 'Dinesh'}

5. popitem()- Remove and return a (key, value) pair from the dictionary. Pairs are returned in LIFO order Python 3.7 onward, before that it used to remove a random element.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
print(d.popitem()) #(4, 'Dinesh')
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan'}

6. setdefault(key, default=None)- Method returns the values of the key if it is present in the dictionary. If key is not present in the dictionary then inserts key with a value of default and returns default.

In the following code there is no change in the dictionary.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
print(d.setdefault(3, 'Chirag')) #Chandan
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}

With the following code a new item is added to the dictionary.

d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
print(d.setdefault(5, 'Esha')) #Esha
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh', 5: 'Esha'}

7. update(other)- This method is used to merge dictionary with other dictionary. While merging if the same key is present in other then it overwrites the existing key's value.

names = {4:'Divya', 5:'Esha'}
d = {1:'Ashish', 2:'Bhavya', 3:'Chandan', 4:'Dinesh'}
print(d) #{1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Dinesh'}
print(names) #{4: 'Divya', 5: 'Esha'}
d.update(names)
print(d) # {1: 'Ashish', 2: 'Bhavya', 3: 'Chandan', 4: 'Divya', 5: 'Esha'}

8. values()- Returns a view object of the dictionary's values.

city = {'NDL': 'New Delhi', 'KOL': 'Kolkata', 'MUM': 'Mumbai', 'HYD': 'Hyderabad'}
print(city.values())

Output

dict_values(['New Delhi', 'Kolkata', 'Mumbai', 'Hyderabad'])

That's all for this topic Dictionary in Python With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. How to Iterate Dictionary in Python
  2. List Comprehension in Python With Examples
  3. Concatenating Lists in Python
  4. Tuple in Python With Examples
  5. Named Tuple in Python

You may also like-

  1. Magic Methods in Python With Examples
  2. Python String replace() Method
  3. Passing Object of The Class as Parameter in Python
  4. Inheritance in Python
  5. Bounded Type Parameter in Java Generics
  6. static Block in Java
  7. Spring Web MVC Tutorial
  8. Angular Reactive Form Validation Example

Wednesday, November 13, 2024

List Comprehension in Python With Examples

List comprehension in Python is a simple way to create a List from an iterable, it consists of an expression, a loop, an iterable that is iterated using the loop and an optional condition enclosed in a bracket.

Syntax of list comprehension in Python is as given below-

other_list = [expression for iterable_items if condition]

Here for loop is used to generate items for the list by iterating the items of the given iterable. Optional condition is used to filter items if required.

The syntax of list comprehension is equivalent to the following traditional way of list creation-

other_list = []
for item in iterable:
 if condition:
  other_list.append(expression_using_item)

List comprehension Python examples

1. A simple example using the for loop with a range function to create a list of numbers with in a given range.

num_list = [num for num in range(1, 5)]
print(num_list) # [1, 2, 3, 4]

2. Creating a list by iterating alphabets of a word in a String.

name = 'Michael'
alphabet_list = [a for a in name]
print(alphabet_list) #['M', 'i', 'c', 'h', 'a', 'e', 'l']

3. Creating list from another list where each element of the list is multiplied by 2.

num_list = [2, 4, 6, 8]

another_list = [num * 2 for num in num_list]
print(another_list) # [4, 8, 12, 16]

4. List comprehension example of list creation using another list where if condition is also used to filter out items.

name_list = ['Michael', 'Jay', 'Jason', 'Ryan']
another_list = [a for a in name_list if len(a) >= 5]
print(another_list) #['Michael', 'Jason']

5. Sum of the items of two lists to create a new list using list comprehension.

num_list1 = [1, 2, 3, 4]
num_list2 = [5, 6, 7, 8]

another_list = [num_list1[i]+num_list2[i] for i in range(len(num_list1))]
print(another_list) #[6, 8, 10, 12]

6. Find common elements between two lists using list comprehension.

num_list1 = [1, 2, 3, 4]
num_list2 = [4, 8, 2, 5]

common_list = [a for a in num_list1 for b in num_list2 if a == b]
print(common_list) # [2, 4]

Which is equivalent to the following code.

common_list = []
for a in num_list1:
    for b in num_list2:
        if a == b:
            common_list.append(a)
print(common_list)

Finding common elements can also be done in the following way-

common_list = [a for a in num_list1 if a in num_list2]
print(common_list)

That's all for this topic List Comprehension in Python With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. List in Python With Examples
  2. Tuple in Python With Examples
  3. Python Conditional Statement - if, elif, else Statements
  4. Python Generator, Generator Expression, Yield Statement
  5. Magic Methods in Python With Examples

You may also like-

  1. Variable Length Arguments (*args), Keyword Varargs (**kwargs) in Python
  2. Python Program to Check Armstrong Number
  3. Interface in Python
  4. Accessing Characters in Python String
  5. How LinkedList Class Works Internally in Java
  6. Java StampedLock With Examples
  7. Constructor Chaining in Java
  8. How to Create PDF From XML Using Apache FOP

Monday, November 11, 2024

Spring Boot Microservice + API Gateway + OAuth2 + Keycloak Server

In this tutorial we'll see how to secure your Spring Boot microservices using Keycloak as Identity and access management server, OUTH2 for authorization and OpenID Connect which verifies the identity of users based on the authentication performed by an authorization server. Rather than securing individual microservices it is better to perform security at the API gateway level, making gateway as a single implementation point to provide cross cutting concerns to all the microservices such as security.

Using Keycloak

By using Keycloak you can have a separate authorization server for authentication and authorization, which means keeping your security logic separate from business logic and delegating all the security related tasks to the Keycloak server which acts as an IAM.

Install keycloak

Convenient way to use keycloak is to run it in a Docker container by using the following command. You can read more about it here.

docker run -d -p 7080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:26.0.5 start-dev

Exposed port is kept as 7080 as 8080 is usually already in use if you are using Spring Boot REST API with services deployed in Tomcat.

Username and password both are given as admin.

You can also download and install it in your local system. Get more information about it here.

Start admin console

Once started you can access Keycloak using the URL- http://localhost:7080. It takes you to the login page where you can login using the credentials as admin. Once logged in you are directed to the admin console.

Here you need to configure Keycloak to perform authentication and authorization. Note that with this setup the configuration done is temporary (kept in memory) which will be lost once you logout. To persist your configuration, you can use a DB server to store it.

Create realm

First thing is to create a realm, on the left-hand side you should see a dropdown with master as the current realm. Click the dropdown and then select "Create realm".

keycloak realm

Realm helps in grouping applications and users with in a realm and keeping it isolated. Keycloak suggests not to create clients and users with in Master realm. Use Master realm only for managing Keycloak not any other application. So, create a new realm and give a Realm name, I have given name as microservice-realm.

Once realm is created that becomes the default realm.

Create client

In order for an application or service to utilize Keycloak it has to register a client in Keycloak. Click on "Clients" option from the menu and select client type as "Open ID Connect".

For Client ID give a name for your client application. Same way enter name and description.

keycloak client

Click next.

Ensure client authentication is turned on. In the authentication flow select "Standard Flow" which is Authorization Code Grant type. Uncheck any other authentication flow option.

keycloak client flow

Click next.

The Valid redirect URIs, is a valid URI, browser can redirect to after a successful login. Since we are going to use Postman and there is no UI application as such so for now you can use * to mean any URI. For testing purpose you can use "*" as value for POst Logout Redirect URI and Web Origins.

keycloak client URI

Click save to save the new client.

If you go to the credentials tab you should be able to see how client is authenticated and what is client secret. We'll need the client's name and secret when trying to access the REST endpoint.

keycloak client credentials

Create user with password

Select Users from the left-hand side section and then click on create new user. User is needed to authenticate against the Keycloak server.

With in the general section give value for Username and email. I have given as test_user and testuser@test.com. Then give values for First name and Last name. Also set the Email Verified to ON signifying that the user is already verified. Click on Create.

keycloak user

Select Credentials to set password for the created User. Enter value for password and set temporary to off so that you are not asked to change password later.

keycloak user password

Same way create another user test_admin with its own password.

Create realm roles

Select Realm roles from the left-hand side section and enter values for the role. I have created two roles user and admin.

Mapping role with user

Go back to users and select test_user. Click on "Role Mapping" tab and then click "Assign role" button.

keycloak user role

In the filter select filter by realm roles. Select "user" role for test_user.

Same way select "admin" role for test_admin user.

With that our Keycloak configuration is complete. Just to reiterate what we have done is-

  1. Created a realm.
  2. Created a client with client secret.
  3. Created a user.
  4. Created roles which are then mapped with the user.

Keycloak Authorization Code Grant Type flow

Oauth 2.0 specification defines several grant types which refers to the way IDToken/Access Token is granted. Some of the grant types are-

  1. Implicit Grant
  2. Authorization Code Grant
  3. Client Credentials Grant
  4. Device Authorization Grant

In this example we are using the Authorization Code Grant type which is optimized for confidential clients (Web Applications) and it is a redirection-based flow. In authorization code grant type first an authorization code is issued by authorization server using which an access token is requested from the authorization server.

OAuth roles in this scenario

OAuth specification defines the following roles-

  1. Resource server- It is the server that hosts the protected resources. Resource server accepts and responds to protected resource requests using access tokens.
  2. Client- It is an application which makes protected resource requests on behalf of the resource owner and with its authorization.
  3. Authorization server- The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.
  4. Resource owner- Resource owner (typically an end-user) owns the resource and capable of granting access to a protected resource.

In our example gateway server acts as both client and resource server, Keycloak is the Authorization server.

Auth code flow

Code changes

Refer this post Spring Boot Microservice + API Gateway + Resilience4J for getting the code to configure API Gateway in Microservices using Spring Cloud Gateway and the other microservices. We'll be adding security layer on top of that.

Adding dependencies

Need to add the following 3 Spring Boot starters for security in the gateway service.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

Changes in configuration

The Spring OAuth2 Resource Server need to verify incoming JWT tokens for that we need to configure the JSON Web Key Set (JWKS) endpoint. With in the gateway-service open the application.yml and add the following configuration.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: "http://localhost:7080/realms/microservice-realm/protocol/openid-connect/certs"  

You can get jwk-set-uri by going to Keycloak admin console, there go to "Realm Settings" and all the way down in the displayed screen. There click on OpenID Endpoint Configuration. There take the value for the jwks_uri property.

Adding Security configuration class

Create a Configuration class annotated with @Configuration because we need to declare @Bean methods. Also use @EnableWebFluxSecurity to add Spring Security WebFlux support. WebFlux because Spring Cloud Gateway is built using the reactive version of the Spring web module.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;

import reactor.core.publisher.Mono;

import org.springframework.core.convert.converter.Converter;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
  @Bean
  SecurityWebFilterChain filterChain(ServerHttpSecurity httpSecurity) throws Exception {
    httpSecurity.authorizeExchange((exchanges) -> exchanges
                .pathMatchers(HttpMethod.POST, "/customer").hasRole("admin")
                .pathMatchers("/account/**").authenticated()
        .pathMatchers("/customer/**").authenticated())
        .oauth2ResourceServer(oAuth2 -> oAuth2
                        .jwt(jwtSpec -> jwtSpec.jwtAuthenticationConverter(grantedAuthoritiesExtractor())));
    httpSecurity.csrf(ServerHttpSecurity.CsrfSpec::disable);
    
    return httpSecurity.build();
    
  }
  
    private Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new AuthServerRoleConverter());
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }
}

Note that as per configuration, only the user having the role as admin can access POST request going to /customer. For other requests , only authentication is required there is no authorization.

oauth2ResourceServer() method verifies an access token before forwarding request to services kept behind the API gateway. Since we have also configured some roles for the users so that information is also passed to the resource server in the form of SimpleGrantedAuthority objects.

import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class AuthServerRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>>{

  @Override
  public Collection<GrantedAuthority> convert(Jwt jwt) {
    Map<String, Object> realmAccess = jwt.getClaim("realm_access");
        if (realmAccess == null || realmAccess.isEmpty()) {
            return new ArrayList<>();
        }
        List<String> roles = (List<String>) realmAccess.get("roles");
        Collection<GrantedAuthority> grantedAuthorities = roles.stream()
            .map(roleName -> "ROLE_" + roleName)
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        //System.out.println(grantedAuthorities);
        return grantedAuthorities;
  }
}

Test using Postman

Create a GET request for URL- localhost:8181/customer/2 (Please make the request as per the ID you have, I have a Customer with ID 2 in DB).

Try sending the request to backend without setting any type of authorization configuration. You should get "401 Unauthorized" as response.

Postman GET request_unauthorized

Authorization settings in Postman

Select "Authorization" tab in Postman and below that select type as Oauth 2.0 and "Add authorization data to" as Request Header.

In the "Configure new token" section provide the following configuration options.

Token Name- accesstoken

Grant Type- Select Authorization code

Callback URL- Check Authorize using browser (So that login page is opened in a browser)

To get values for these two properties "Auth URL" and "Access Token URL " go to "Realm Settings" and all the way down in the displayed screen. There click on OpenID Endpoint Configuration

Keycloak Accesse end points

Select value of authorization_endpoint and enter it in Auth URL, select value of token_endpoint and enter it in Access Token URL

Client ID- Name selected for the client (customer-client). Get it from Keycloak admin console.

Client Secret- Client secret displayed for the client. Get it from Keycloak admin console.

Scope- openid email profile (scope for access is email and profile of the user)

State- ab12-cd34-ef56 (Give an alphanumeric value that is used for preventing cross-site request forgery.

Client Authentication- select "send client credentials in body"

Then click "Get New Access Token"

Postman authorization config

Ensure that you are not already logged in to the Keycloak server in your default browser as admin, otherwise it will show you already logged in as admin. What we need is to log in as either test_user or test_admin.

Once you click on "Get New Access Token" in Postman it should open the Keycloak login page. Lets login with test_user and its password.

keycloak login

Once you enter the credentials and click sign in browser should redirect you to Postman with the access token. If that doesn't happen check that browser is allowing popups for this URL.

Postman with access_token

Click on use token so that the sent token is used as the bearer token with the request. Click Send to send the GET request to the backend. This time you should get the Customer details.

Postman GET request_authorized

POST mapping

Create a Post request for URL- localhost:8181/customer with body as

{
    "name": "Suresh",
    "age": 35,
    "city": "Bangalore"
}

Authorization setting remains same and user logged in is "test_user"

Click on use token so that the sent token is used as the bearer token with the request. Click Send to send the Post request to the backend.

Since "user" role doesn't have authority to make a POST request so 403 Forbidden is sent as response.

Click again on "Get New Access Token" and login as "test_admin" user. Now POST request should work.

Postman POST request

That's all for this topic Spring Boot Microservice + API Gateway + OAuth2 + Keycloak. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Observability - Distributed Tracing, Metrics
  2. Spring Boot Event Driven Microservice With Kafka
  3. Spring Boot Microservice - Externalized Configuration With Spring Cloud Config
  4. Spring Boot Microservice Circuit Breaker Using Resilience4j
  5. Spring Boot Microservice Example Using WebClient

You may also like-

  1. Spring MVC Checkbox And Checkboxes Form Tag Example
  2. Spring NamedParameterJdbcTemplate Insert, Update And Delete Example
  3. Injecting Inner Bean in Spring
  4. Bean Definition Inheritance in Spring
  5. Java LinkedBlockingDeque With Examples
  6. Abstraction in Java
  7. Angular Event Binding With Examples
  8. JavaScript Rest Parameter

Monday, November 4, 2024

Spring MVC @RequestParam Annotation Example

In this Spring MVC @RequestParam annotation example we’ll see how request data which is passed as query parameter in the URI can be extracted using @RequestParam annotation.

Spring @RequestParam and @RequestMapping annotation

If you are passing request parameters as query parameters, then you can use @RequestParam along with @RequestMapping annotation in the Spring controller method to get value of those request parameters.

For example- /spring-mvc/getUser?userId=101

Here userId is the query parameter that can be retrieved using the @RequestParam annotation.

Spring web MVC example with @RequestParam annotation

In this Spring MVC example we’ll have a JSP (home page) with 3 fields, entered values in these 3 fields are sent as query parameters with in the URL.

home.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Spring MVC tutorial - Home JSP</title>
</head>
<body>
  <div>Message- ${message}</div>
  <form name="userform" action="showUser" method="get">
    <table>
      <tr>
        <td>
          First Name: <input type="text" id="firstName" name="firstName">
        </td>
      </tr>
      <tr>
        <td>
          Last Name: <input type="text" id="lastName" name="lastName">
        </td>
      </tr>
      <tr>
        <td>
          DOB: <input type="text" id="dob" name="dob">
        </td>
      </tr>        
    </table>               
    <input type="submit" value="Submit">
  </form>
</body>
</html>

Spring MVC - Controller class

In the controller class there are two methods. First method showHome() is annotated with RequestMapping value parameter as “/” and returns the logical view name as "home" which results in the display of home.jsp.

Another method showUser() serves the request where path is “/showUser”. In this method the method parameters are annotated with @RequestParam. The value parameter with in the @RequestParam should match the query parameter name. For example this handler method will serve the requests in the form - /showUser?firstName=Leonard&lastName=Nimoy&dob=1956-05-23

The value of the query parameter will be assigned to the corresponding method parameter.

MessageController.java

import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class MessageController {
 @RequestMapping(value = "/", method = RequestMethod.GET)
 public String showHome(Model model) {
  model.addAttribute("message", "MVC Example with dynamic URL");
  return "home";
 }
 
 @RequestMapping(value = "/showUser", method = RequestMethod.GET)
 public String showUser(@RequestParam("firstName") String firstName, 
                @RequestParam("lastName") String lastName, 
                @DateTimeFormat(pattern = "yyyy-MM-dd")
                @RequestParam("dob") LocalDate dob,
                Model model) { 

  model.addAttribute("firstName", firstName);
  model.addAttribute("lastName", lastName);
  model.addAttribute("dob", dob);
  return "user";
 }
}

The parameters of the method showUser() which have the values of the query parameters assigned to them are added to the Model. The method returns the logical view name as "user" which is resolved to the view user.jsp.

Note that if method parameter name is same as the query parameter name in the @RequestMapping then the value parameter with @RequestParam is optional. So the same method can also be written as-

@RequestMapping(value = "/showUser", method = RequestMethod.GET)
public String showUser(@RequestParam String firstName, 
             @RequestParam String lastName, 
             @DateTimeFormat(pattern = "yyyy-MM-dd")
             @RequestParam LocalDate dob,
             Model model) {

 ...
 ...
}

user.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Spring MVC tutorial - User</title>
</head>
<body>
<div>First Name: ${firstName}</div>
<div>Last Name: ${lastName}</div>
<div>DOB: ${dob}</div>
</body>
</html>

Home page

Spring MVC with @RequestParam

User page

Spring MVC with RequestParam annotation

Default value for RequestParam parameters in Spring MVC

If you want to provide default value for the RequestParam parameters if the query parmeters are not there in the request then you can use defaultValue attribute of the @RequestParam. The default value is used as a fallback when the request parameter is not provided or has an empty value.

@RequestMapping(value = "/showUser", method = RequestMethod.GET)
public String showUser(
  @RequestParam(value="firstName", defaultValue="Test") String firstName, 
  @RequestParam(value="lastName", defaultValue="User") String lastName, 
  @DateTimeFormat(pattern = "yyyy-MM-dd")
  @RequestParam(value="dob", defaultValue="2000-01-01") LocalDate dob,
  Model model) { 
 ...
 ...
}

required attribute in Spring @RequestParam annotation

You can set whether the parameter is required or not using required attribute. Default value for the required attribute is true, which means an exception is thrown if the parameter is missing in the request. By setting required as false null value is assigned if the parameter is not present in the request.

For example if you want to make DOB as optional.

@RequestMapping(value = "/showUser", method = RequestMethod.GET)
public String showUser(
  @RequestParam(value="firstName", defaultValue="Test") String firstName, 
  @RequestParam(value="lastName", defaultValue="User") String lastName, 
  @DateTimeFormat(pattern = "yyyy-MM-dd")
  @RequestParam(value="dob", required=false) LocalDate dob,
  Model model) {
 ...
 ...
}

That's all for this topic Spring MVC @RequestParam Annotation Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Web MVC Tutorial
  2. Spring MVC Example With @PathVaribale - Creating Dynamic URL
  3. Spring MVC File Download Example
  4. Spring MVC Redirect Example
  5. Spring Transaction Management JDBC Example Using @Transactional Annotation

You may also like-

  1. Internationalization (i18n) Using MessageSource in Spring
  2. @Resource Annotation in Spring Autowiring
  3. Spring Bean Life Cycle
  4. Bean Definition Inheritance in Spring
  5. Difference Between ArrayList And CopyOnWriteArrayList in Java
  6. Java ReentrantLock With Examples
  7. this Keyword in Java With Examples
  8. Checking Number Prime or Not Java Program

Sunday, November 3, 2024

Java Stream - min() With Examples

In Java Stream API there is a min() method that is used to get the minimum element of this stream according to the provided Comparator. In this post we’ll see some examples of the min() method.

Syntax of the Stream.min() method

min is a terminal operation and its syntax is as given below-

Optional<T> min(Comparator<? super T> comparator)

Here comparator argument is an implementation of the Comparator to compare elements of this stream.

Method returns an Optional describing the minimum element of this stream, or an empty Optional if the stream is empty.

Stream.min is considered a special case of a reduction operation as it takes a sequence of input elements and combines them into a single summary result.

min() method Java examples

1. Finding min value from a stream of numbers.

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class StreamMin {
  public static void main(String[] args) {
    List<Integer> numList = Arrays.asList(7, 9, 14, 1, 59, 23, 77, 10, 12, 4);
      Optional<Integer> minElement = numList.stream().min(Integer::compare);
      if(minElement.isPresent()){
        System.out.println("Minimum element: " + minElement.get());
      }
  }
}

Output

Minimum element: 1

2. In the following example custom comparator is passed as an argument to the min() method to get minimum method as per the passed Comparator.

public class StreamMin {
  public static void main(String[] args) {
    List<Integer> numList = Arrays.asList(7, 9, 14, 1, 59, 23, 77, 10, 12, 4);
      Optional<Integer> minElement = numList.stream().min(new MyComparator());
      if(minElement.isPresent()){
        System.out.println("Minimum element: " + minElement.get());
      }
  }
}

class MyComparator implements Comparator<Integer>{
  @Override
  public int compare(Integer o1, Integer o2) {
    return o1.compareTo(o2);
  }
}

Output

Minimum element: 1

3. Using Stream.min() method with custom object. In the example, objects of Employee class are used and the objective is to find minimum salary using the min() method of the Java Stream API.

Employee class used is as given here.

public class Employee {
  private String empId;
  private int age;
  private String name;
  private char gender;
  private int salary;
  Employee(String empId, int age, String name, char gender, int salary){
    this.empId = empId;
    this.age = age;
    this.name = name;
    this.gender = gender;
    this.salary = salary;
  }
  public String getEmpId() {
    return empId;
  }

  public int getAge() {
    return age;
  }

  public String getName() {
    return name;
  }

  public char getGender() {
    return gender;
  }

  public int getSalary() {
    return salary;
  }
}

In the example mapToInt() method is used to get the salary part of the employee object which is then passed to the min() to get the minimum salary.

public class StreamMin {
  public static void main(String[] args) {
    List<Employee> empList = Arrays.asList(new Employee("E001", 40, "Ram", 'M', 5000), 
                new Employee("E002", 35, "Shelly", 'F', 7000), 
                new Employee("E003", 24, "Mark", 'M', 9000), 
                new Employee("E004", 37, "Rani", 'F', 10000),
                new Employee("E005", 32, "Anuj", 'M', 12000));  
     OptionalInt minEmpSal = empList.stream()
                .mapToInt(Employee::getSalary)
                .min();
      if(minEmpSal.isPresent()){
        System.out.println("Minimum salary: " + minEmpSal.getAsInt());
      }
  }
}

Output

Minimum salary: 5000

That's all for this topic Java Stream - min() With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Java Stream - max() With Examples
  2. Java Stream - filter() With Examples
  3. Java Stream - distinct() With Examples
  4. Java Stream - findFirst() With Examples
  5. Java Stream API Interview Questions And Answers

You may also like-

  1. How to Sort ArrayList of Custom Objects in Java
  2. equals() And hashCode() Methods in Java
  3. Type Casting in Java With Conversion Examples
  4. StringJoiner Class in Java With Examples
  5. Writing a File Asynchronously Java Program
  6. Spring Integration With Quartz Scheduler
  7. Operator Overloading in Python
  8. Angular Access Control CanActivate Route Guard Example