Spring security provides authentication an authorisation.
Spring Security is a security framework for Java and Spring Boot applications.
It provides:
Authentication → verifying user identity
Authorization → controlling access
Session management
Password encryption
CSRF protection
JWT/OAuth2 support
Spring security will be enabled after adding below XML defintions in POM.xml.
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-security-test
test
Spring security filters almost all pages except the pages which is marked to permit and gives login page.
Type 1:
Passwor will be automatically generate in devleoper tool console and user will be user.
This password will be changedd on each application bootup.
Flow :
http://localhost:8080/First/add/6/8
when the user clicks this link, instead of displaying output a login page will be shown, this is prewritten and plugged in using xml definition. the actual output of addition will be displayed after authentication.
Type:2
Adding our own username and password in application properties.
spring.security.user.name=admin
spring.security.user.password=generate
Now application will not generate password but a login page will be shown on clicking the addition endpoint.
User has to enter correct username and password mentioned in application.properties. After authenticating him, he will be redirected to the output page,else error with status code 403 will be displayed
Type3:
Adding multiple users.
we cannot add all users in property file. Though property file will not accept adding duplicate properties, in real time we will be having huge data and adding in property or convincing it to accept is not an idea.
So we can achieve this multiple user concept in spring security using UserDetailsManager Interface. Its implementation class is InMemoryUserDetailsManager.
This accepts a list with userDetails objects.
UserDetails is a static method with methods to add username,password,roles etc.
Here is the code snippet:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@bean
public UserDetailsManager createUserDetailsManager(){
UserDetails user1= User.withUsername("Sri").password("{noop}generate").roles("admin").build();
UserDetails user2=User.withUsername("Desigan").password("{noop}generate").roles("user").build();
// Add users to the UserDetailsManager
List l = Arrays.asList(user1, user2);
UserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(l);
return userDetailsManager;
}
Authorisation:
It works based on user privilages.
User with admin access can see all pages.
User with read access can view pages but he cannot make any changes.
we can set these privilages in security config class.
@bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// Disable CSRF (useful for REST APIs)
.csrf(csrf -> csrf.disable())
// Authorization rules
.authorizeHttpRequests(auth -> auth
.requestMatchers("/calc/add/**").permitAll() // No auth required
.requestMatchers("/calc/sub/**").hasRole("USER")
.requestMatchers("/calc/mul/**").hasRole("ADMIN")
.requestMatchers("/calc/div/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
In above snippet ".permitAll()" allows the user to view endpoint without any authentication.
For all the requestMatchers("/div/").hasRole("user") -> user with User role will be able to view this page.
**Option 2: JDBCUserDetialsManager.
It is nothing but communicating with database to perform login.
It has four steps, 1. collect data from UI 2. Request Database for UserDetails using loadByUserName(username) method from UserDetailService interface.
- compare data from UI with data from DB
- Provide success or failure login based on the results from above step. To achieve this we need load data into DB first, This is what we call as "SIGNUP". Im going to write an endpoint and send a request from POSTMan and load data into Database. This request again pass through Spring security firewall check. To allow our request to endpoint we can request spring security to permit our endpoint URL by adding below line.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/error").permitAll()
.requestMatchers("/First/signUp/**").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
// .httpBasic(Customizer.withDefaults());
return http.build();
}
Controller:
@RequestMapping("/signUp")
////@ResponseBody
public ResponseEntity processUserSignUp(@RequestBody User user ){
log.info("inside LearningFrontController.getmultiplyService");
response=calculatorService.processUserSignUp(user);
log.info(response);
if(response.equalsIgnoreCase("success")){
return ResponseEntity.status(200).body(response);
}
log.info("exiting LearningFrontController.getmultiplyService");
return ResponseEntity.status(400).body("Signup failed");
}
Service:
public String processUserSignUp(User user){
String response="failed";
UserDetailsEntity userDetailsEntity = new UserDetailsEntity();
userDetailsEntity.setUserName(user.getUserName());
String maskedPassword=passwordEncoder.encode(user.getPassword());
userDetailsEntity.setMaskedPassword(maskedPassword);
userDetailsEntity.setUserRole(user.getRoles());
UserDetailsEntity userDetailsEntity1= customUserDetailsRepo.save(userDetailsEntity);
if(userDetailsEntity1!=null) {
return "success";
}
return response;
}
JSON:
{
"userName":"Sri",
"Password":"generate",
"roles":"User"
}
What we have in table?
UserNo PAssword userName Role
202 $2a$10$PtbhgrteGOLk0azcyGK1x SriKamini USER
203 $2a$10$WcefaBSrdqTIqLLOAh3XNep Sri USER
Authenticate and Generate Token if authencation passes
Step 1. *implement UserDetailsService Interface *
public class CustomUserDetailService implements UserDetailsService {
@Autowired
CustomUserDetailsRepo customUserDetailsRepo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<UserDetailsEntity> userDetailsEntityList = customUserDetailsRepo.findByUserName(username);
if (userDetailsEntityList == null)
return null;
else {
UserDetailsEntity userDetailsEntity = userDetailsEntityList.get();
//convert userDetailsEntity to UserDetails
UserDetails userDetails = User.withUsername(userDetailsEntity.getUserName())
.password(userDetailsEntity.getMaskedPassword())
.roles(userDetailsEntity.getUserRole()).build();
return userDetails;
}
}
}
Step2 IncluDe DAOAuthenticationProviDer in security Config
Getting UserDetailsService in Below Snippet
@bean
public UserDetailsService userDetailsService(){
return new CustomUserDetailService();
}
//Passing this generate CustomUserDetailService to DaoAuthenticationProvider
@bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider
=new DaoAuthenticationProvider(userDetailsService());
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
return daoAuthenticationProvider;
}
//Creating an instance AuthenticationManager
@bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config)
throws Exception {
return config.getAuthenticationManager();
}
Step3
Perform login from POSTMAN,
This mentioneD Snippet will authenticate username & passwor coming in RequestBody.
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User request) {
log.info("inside login");
log.info("request.getUserName(), "+request.getUserName() + request.getPassword() + request.getPassword());
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUserName(),
request.getPassword()
)
);
String token = "";
log.info("authentication.isauthenticated"+authentication.isAuthenticated());
if(authentication.isAuthenticated()) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
token = jwtUtils.generateToken(String.valueOf(userDetails));
}
return ResponseEntity.ok(token);
}
PostMan JSON
{
"userName": "SriKamini",
"Password": "generate"
}
output:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJvcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5LmNvcmUudXNlcmRldGFpbHMuVXNlciBbVXNlcm5hbWU9U3JpS2FtaW5pLCBQYXNzd29yZD1bUFJPVEVDVEVEXSwgRW5hYmxlZD10cnVlLCBBY2NvdW50Tm9uRXhwaXJlZD10cnVlLCBDcmVkZW50aWFsc05vbkV4cGlyZWQ9dHJ1ZSwgQWNjb3VudE5vbkxvY2tlZD10cnVlLCBHcmFudGVkIEF1dGhvcml0aWVzPVtST0xFX1VTRVJdXSIsImlhdCI6MTc3OTQ2ODEzNSwiZXhwIjoxNzc5NDY4NzM1fQ.EcUWMgOumkHi3Qo7Ggq7ju8FXqvrNob6_YSOm7WmbYs
Login 2 Authorise the username & password coming in authorise tab in POSTMan. i.e credentials coming in authorise tab
Login :
- Rest Login using UserName an password in Request body
- Rest Login using UserName an password in Autorisation tab an request body can be anything & an it can be empty
- Rest Login using bearer token.
- Custom Login - Overrides default spring security login and it takes userdefined custom login. a.After custom Login redirect to home page b. After custom login redirect to the same endpoint which called custom login.
_ Rest Login using UserName an password in Request body_
** Rest Login using UserName an password in Autorisation tab an request body can be anything & an it can be empty**
Security config takes " .httpBasic(Customizer.withDefaults())"
SecurityConfig.class:
@bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/error").permitAll()
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.addFilterBefore(
jwtAuthFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
AuthController.java
@RestController
@RequestMapping("/api/1.0/security")
public class AuthController {
private static final Logger log= LoggerFactory.getLogger(AuthController.class);
@Autowired
private AuthenticationManager authenticationManager;
@PostMapping("/perform_login")
public ResponseEntity<?> login(@RequestBody User request) {
log.info("inside login");
log.info("request.getUserName(), "+request.getUserName() + request.getPassword() + request.getPassword());
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUserName(),
request.getPassword()
)
);
String token = "";
log.info("authentication.isauthenticated"+authentication.isAuthenticated());
if(authentication.isAuthenticated()) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
token = jwtUtils.generateToken(userDetails.getUsername());
}
return ResponseEntity.ok(token);
}
JWTUTil.java
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username) // payload
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 10)) // 10 mins
.signWith(key)
.compact();
}
2.** Rest Login using UserName an password in Autorisation tab an request body can be anything & an it can be empty**
If you enter username and password in authorisation tab,
.httpBasic(Customizer.withDefaults()) will accept the input and handle the request.
3.Rest Login using bearer token.
JWT comes in picture to accept and validate this token.
below snippet is the controller code to accept the token and validate it.
if jwt is enale in security config, JWT Auth filter handles this request an validates even Before it reaches controller
@GetMapping("/token")
public String token(HttpServletRequest request){
System.out.println("insie token");
String authHeader=request.getHeader("Authorization");
log.info("authHeader "+authHeader);
if(authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
return "Invalid Token";
}
doFilterInternal() in JWTAuthFilter accepts all requests using "OncePerRequestFilter". If 1000 requests comes in then all these 1000 request must pass through this filter "OncePerRequestFilter "
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
log.info("insife doFilterInternal");
String authHeader = request.getHeader("Authorization");
String token = null;
String username = null;
log.info("authHeader " + authHeader);
try{
try {
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
log.info("token " + token);
log.info(jwtUtils.toString());
username = jwtUtils.getUserName(token);
log.info("username " + username);
}} catch(Exception ex){
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
if (username != null &&
SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails =
userDetailService.loadUserByUsername(username);
log.info("value of valiate token " + jwtUtils.validateToken(token, userDetails));
log.info("authorities "+userDetails.getAuthorities());
if (jwtUtils.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
log.info("Credentials "+authentication.getCredentials());
log.info("authorities " + userDetails.getAuthorities());
// authentication.setDetails(userDetails);
authentication.setDetails(
new WebAuthenticationDetailsSource()
.buildDetails(request)
);
SecurityContextHolder.getContext()
.setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
catch(
Exception e) {
e.printStackTrace();
}
}
- Custom Login - Overrides default spring security login and it takes userdefined custom login.
database connection object is achieved using
declare this code in security connection class.
@bean
public UserDetailsService userDetailsService(){
return new CustomUserDetailService();
}
@bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider=new DaoAuthenticationProvider(userDetailsService());
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
return daoAuthenticationProvider;
}
@bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config)
throws Exception {
return config.getAuthenticationManager();
}
private final JwtAuthFilter jwtAuthFilter;
public SecurityConfig(JwtAuthFilter jwtAuthFilter) {
this.jwtAuthFilter = jwtAuthFilter;
}
security config also takes Below code
.formLogin(form -> form
.loginPage("/api/1.0/security/login")
.loginProcessingUrl("/api/1.0/security/perform_login")
.defaultSuccessUrl("/First/index", true)
.failureUrl("/loginPage?error=true")
.permitAll())
when defaultSuccessUrl is enaBled.after successful login, home page will be redirected.
if we want to redirect to the enpoint which called the login service,
step 1: http://localhost:8080/First/add/6/8
Spring security custom login page will be displayed.
after entering the credentials,".loginProcessingUrl("/api/1.0/security/perform_login")" will Be processed. JWTAuthFilter will happen and endpoint AuthController an its ,method with request mapping "perform_login" will de called. username authentication will happen here.
Post Login,http://localhost:8080/First/add/6/8, Controller "First"with requestmapping"6 and 8 will be processed and output is displayed.
Can we send RequestParams in POSTMAN?
yes under the Body tab we have form_data tab.
It takes key and value. we can send out requestparams in key as param name and value as its value to be processed.
Top comments (0)