Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Tuesday, February 22, 2022

SecurityContext and SecurityContextHolder in Spring Security.

Main purpose of SecurityContext to hold the currently authenticated user information as
principal and make sure this principal will be available all the method in same thread.
To get authenticated user details SecurityContextHolder will be used.


from principle? Here my spring boot project structure. From this principal we can get the currently logged in user. How can we get the user detail.



I have added spring-boot-starter-security jar in my pom.xml. This to set a default login screen for my application. 

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
If you add this dependency, spring boot will generate the password for your application on
server startup.

You can see the password in console log: Here generated security is below and default username will be 'user',
Using generated security password: e3660fc9-941c-4012-ba6d-8d1870a177d6. If you restart the server then new password will be generate.

Let we check the principal for default user and password for security context.



Here we are able to get the username of currently logged in user using securitycontext and securitycontextholder. 

We cannot every time copy the password from console. Spring provide the properties to set username and password. Here I used them for my application security. Let we check principal after change this property change.

spring.security.user.name=username
spring.security.user.password=User@123

Let login the application using new username and password, check the securityContext 
userdetails,




Let check with other method call and find the username for securitycontext.
let me call my getUser() to fetch particular user detail from db,

let me call : http://localhost:8084/getUser?userId=101

Here we can see the username from securityContext. Usually we need to have
securityContextHolder to get securityContext, from securityContext we can get authentication
from there we can get principal.

Here code snippet:

Object principalObject = SecurityContextHolder.getContext().
                                    getAuthentication().getPrincipal();

if(principalObject instanceof org.springframework.security.core.userdetails.User)
{
String userName = ((org.springframework.security.core.userdetails.User)principalObject)
                                                                               .getUsername();
System.out.println("logged in user: " + userName);
}

Main purpose of SecurityContext to hold the currently authenticated user information as
principal and make sure this principal will be available all the method in same thread.
To get authenticated user details SecurityContextHolder will be used.


From this principal we can get the currently logged in user. How can we get the user detail
from principle? Here my spring boot project structure.

Spring @Primary annotation

 @Primary annotation used to specify the preference when multiple beans of same type are qualified. In some cases, in your application may have multiple beans have same type and spring is responsible to autowire a bean. By providing @Primary we can specify with bean by default can be used.

Already we have a annotation to solve these kind of issue right?.. The @Qualifier annotation  we have to specify the bean right! . But its little different from this one. Already we discussed about Qualifier


Qualifier used, spring to autowire specific bean over same type. Primary used to preference to the specific bean among multiple bean of same type.


Here example, I have added a controller class on my existing project and override toString(), I will start with changes done for @Primary, if you not read @Qualifier please spend some time to read it

In ChefService, added @Bean and @Qualifier for


public interface Chef {
String doCook();
}

@Component
@Qualifier("Veg")
public class VegChef implements Chef {
@Override
public String doCook() {
return "Vegetarian Food";
}

@Override
public String toString() {
return "VegChef";
}
}

@Component
@Qualifier("Non-Veg")
public class NonVegChef implements Chef {
@Override
public String doCook() {
return "Non-Vegetarian Food";
}

@Override
public String toString() {
return "NonVegChef";
}
}
Here we are mentioning Veg is Primary bean, so Spring autowire VegChef as preference.
@Service
public class ChefService {

@Bean
@Primary
@Qualifier("Veg")
public Chef vegChef(){
return new VegChef();
}

@Bean
@Qualifier("Non-Veg")
public Chef nonVegChef(){
return new NonVegChef();
}

}
For Example, first I mentioned here chef reference with Non-Veg Qualifier 
which mapped with NonVegChef Bean.
@Controller
public class ChefController {


@Autowired
@Qualifier("Non-Veg")
Chef chef;

@GetMapping(value = "/cook")
@ResponseBody
public String cook(){
System.out.println( "Cook : " + chef);
return "Cook : " + chef;
}
}

Set See Output and server startup log to understand how the bean is autowired in spring.
16:15:13.249 [main] DEBUG org.springframework.beans.factory.annotation
.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name
'chefController' to bean named 'nonVegChef'

Output : Cook : NonVegChef.

Now we confirm that how @Qualifier annotation works, let check how @Primary annotation works
in spring,

Let see the changes in ChefController
@Controller
public class ChefController {


@Autowired
Chef chef;

@GetMapping(value = "/cook")
@ResponseBody
public String cook(){
System.out.println( "Cook : " + chef);
return "Cook : " + chef;
}
}
We have removed @Qualifier from chef reference, and see the output and startup log, 
16:18:49.230 [main] DEBUG org.springframework.beans.factory.annotation
.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 
'chefController' to bean named 'vegChef'
Output: Cook : VegChef

Previous topic @Qualifier Annotation

Thursday, February 17, 2022

Spring @Qualifier Annotation

 There is a possibility to have more than one bean with same type, in this case your IDE will show the error like 'Could not autowire. There is more than one bean of '' Type'.  This says we need to explicitly specify the name for the Object. This can done by using @Qualifier annotation. Spring provide this annotation to specify the name. 

Here I have Chef Interface, VegChef, NonVegChef component implements Chef and override the doCook() method in both component. And trying to autowire in VegChefService service.



public interface Chef {
String doCook();
}

@Component
public class VegChef implements Chef {
@Override
public String doCook() {
return "Vegetarian Food";
}
}

@Component
public class NonVegChef implements Chef {
@Override
public String doCook() {
return "Non-Vegetarian Food";
}
}

@Service
public class VegChefService {
@Autowired
private Chef chef;
}



 Here we will get the error says "Could not autowire. There is more than one bean of 'Chef' type.

Let see how to resolve the problem by using @Qualifier to indicate which bean want to use. we need add @Qualifier code in Component class like below, and need to add @Qualifier in service class to specify the bean name.

@Qualifier("Veg")
public class VegChef implements Chef {
@Override
public String doCook() {
return "Vegetarian Food";
}

} 


@Service
public class VegChefService {
@Autowired
@Qualifier("Veg")
private Chef chef;
}

As conclusion @Qualifier used to resolve more then one bean issue. 

Next topic @Primary Annotation