Spring Security with JWT (Symmetric Encryption)

JwtUtil

package com.springSecurity.jwt;

import com.springSecurity.user.UserDetailsServiceImpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JwtFilter extends OncePerRequestFilter {

	@Autowired
	private JwtUtil jwtUtil;
	@Autowired
	private UserDetailsServiceImpl userDetailsServiceImpl;

	@Override
	protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
			FilterChain filterChain) throws ServletException, IOException {

		String authorizationHeader = httpServletRequest.getHeader("Authorization");

		String token = null;
		String userName = null;

		if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
			token = authorizationHeader.substring(7);
			userName = jwtUtil.extractUsername(token);
		}

		if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {

			UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(userName);

			if (jwtUtil.validateToken(token, userDetails)) {

				UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
						userDetails, null, userDetails.getAuthorities());
				usernamePasswordAuthenticationToken
						.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
				SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
			}
		}
		filterChain.doFilter(httpServletRequest, httpServletResponse);
	}
}

JwtFilter

package com.springSecurity.jwt;

import com.springSecurity.user.UserDetailsServiceImpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JwtFilter extends OncePerRequestFilter {

	@Autowired
	private JwtUtil jwtUtil;
	@Autowired
	private UserDetailsServiceImpl userDetailsServiceImpl;

	@Override
	protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
			FilterChain filterChain) throws ServletException, IOException {

		String authorizationHeader = httpServletRequest.getHeader("Authorization");

		String token = null;
		String userName = null;

		if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
			token = authorizationHeader.substring(7);
			userName = jwtUtil.extractUsername(token);
		}

		if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {

			UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(userName);

			if (jwtUtil.validateToken(token, userDetails)) {

				UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
						userDetails, null, userDetails.getAuthorities());
				usernamePasswordAuthenticationToken
						.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
				SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
			}
		}
		filterChain.doFilter(httpServletRequest, httpServletResponse);
	}
}

AuthRequest.java

package com.springSecurity.jwt;

public class AuthRequest {
	
	private String userName;
	private String password;
	
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}	
}

JWTController.java

package com.springSecurity.jwt;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JWTController {
	
	@Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private AuthenticationManager authenticationManager;
	
	@PostMapping("/authenticate")
	public String generateToken(@RequestBody AuthRequest authRequest) throws Exception {
		try {
			authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUserName(), authRequest.getPassword()));
		} catch (Exception ex) {
			throw new Exception("inavalid username/password");
		}
		return jwtUtil.generateToken(authRequest.getUserName());
	}
}

SecurityConfig.java

package com.springSecurity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.springSecurity.jwt.JwtFilter;
import com.springSecurity.user.UserDetailsServiceImpl;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "myproject.security.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	UserDetailsServiceImpl userDetailsServiceImpl;

	@Autowired
	private JwtFilter jwtFilter;

	/**
	 * @Override protected void configure(AuthenticationManagerBuilder auth) throws
	 *           Exception { auth .inMemoryAuthentication()
	 *           .withUser("admin1").password(passwordEncoder().encode("admin")).roles("ADMIN").and()
	 *           .withUser("admin2").password(passwordEncoder().encode("admin")).roles("ADMIN").authorities("MANAGER").and()
	 *           .withUser("user1").password(passwordEncoder().encode("user")).roles("USER").and()
	 *           .withUser("user2").password(passwordEncoder().encode("user")).roles("USER").authorities("LEAD");
	 *           }
	 */

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authenticationProvider());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		http.csrf().disable();

		http.authorizeRequests().antMatchers("/").permitAll()// bypass authetication and authorization
				.antMatchers("/authenticate").permitAll().antMatchers("/empList").permitAll()// bypass authetication and
																								// authorization
				.antMatchers("/empList2").permitAll()// bypass authetication and authorization
				.antMatchers("/profile").authenticated()// Authentication only required
				.antMatchers("/user/**").hasRole("USER")// Authetication and Authorization required
				.antMatchers("/admin/**").hasRole("ADMIN")// Authetication and Authorization required
				.antMatchers("/useroradmin").hasAnyRole("ADMIN", "USER")// Either admin or user role required
				// permissions
				.antMatchers("/listTeam").hasAuthority("LEAD")// Authetication, Authorization (since /user/** is
																// required) and permission required
				.antMatchers("/listEmp").hasAuthority("MANAGER")// Authetication, Authorization and permission required
				.antMatchers("/**").denyAll().and().httpBasic();
		http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

		http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
	}

	@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Bean
	DaoAuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
		daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
		daoAuthenticationProvider.setUserDetailsService(userDetailsServiceImpl);
		return daoAuthenticationProvider;
	}

	@Bean
	PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
//		return new BCryptPasswordEncoder();
	}

}

User.java

package com.springSecurity.user;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class User {
	
	private String id;
	private String username;
	private String password;
	private String roles;
	private String permissions;
	
	public User() {
		
	}
	public User(String id, String username, String password, String roles, String permissions) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.roles = roles;
		this.permissions = permissions;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getRoles() {
		return roles;
	}
	public void setRoles(String roles) {
		this.roles = roles;
	}
	public String getPermissions() {
		return permissions;
	}
	public void setPermissions(String permissions) {
		this.permissions = permissions;
	}
	public List<String> getRolesList(){
		List<String> arrayList = new ArrayList<>(); 
		if(this.roles.length()>0) {
			Collections.addAll(arrayList,this.roles.split(",")); 
		}
		return arrayList;
	}
	
	public List<String> getPermissionsList(){
		List<String> arrayList = new ArrayList<>(); 
		if(this.roles.length()>0) {
			Collections.addAll(arrayList,this.permissions.split(",")); 
		}
		return arrayList;
	}
}

UserDAO.java

package com.springSecurity.user;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAO {
	
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	public User getEmpDetails(String userName) {
		StringBuilder query = new StringBuilder();
		query.append("select EMP_ID, EMP_USERNAME, EMP_PASS, EMP_ROLES, EMP_PERMISSIONS from emp_temp where EMP_USERNAME=?");
		User user =jdbcTemplate.queryForObject(query.toString(),new Object[] {userName}, new RowMapper<User>() {

			@Override
			public User mapRow(ResultSet rs, int rowNum) throws SQLException {
				return new User(
						rs.getString("EMP_ID"),
						rs.getString("EMP_USERNAME"),
						rs.getString("EMP_PASS"),
						rs.getString("EMP_ROLES"),
						rs.getString("EMP_PERMISSIONS"));
			}
			
		});
		return user;
	}
}

UserDetailsImpl.java

package com.springSecurity.user;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class UserDetailsImpl implements UserDetails{
	
	private static final long serialVersionUID = 1L;

	private User user;
	
	public UserDetailsImpl(User user) {
		this.user = user;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> authorityList = new ArrayList<>();
		this.user.getPermissionsList().forEach(p -> {
			GrantedAuthority authority = new SimpleGrantedAuthority(p);
			authorityList.add(authority);
		});
		
		this.user.getRolesList().forEach(p -> {
			GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_"+p);
			authorityList.add(authority);
		});
		
		return authorityList;
	}

	@Override
	public String getPassword() {
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		return user.getUsername();
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}
}

UserDetailsServiceImpl.java

package com.springSecurity.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserDetailsServiceImpl implements UserDetailsService{

	@Autowired
	UserDAO userDAO;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userDAO.getEmpDetails(username);
		UserDetailsImpl userPrincipal = new UserDetailsImpl(user);
		return userPrincipal;
	}
}

Controller.java

package com.springSecurity;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.security.RolesAllowed;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {
	
	/**
	 * Accessable without Authentication and authorization
	 * @return
	 */
	@GetMapping("/")
	public String getWelcomePage() {
		return "Hello....welcome to our website";
	}
	
	/**
	 * All logged in users
	 * Accessable with authentication but authorization not required
	 * @return
	 */
	@GetMapping("/profile")
	public String getProfile() {
		return "Welcome to profile page";
	}
	
	/**
	 * Only logged in user with role USER can access this
	 * @return
	 */
	@GetMapping("/user")
	public String getUser() {
		return "Hello User Role";
	}
	
	/**
	 * Only loggged in admin can access this
	 * @return
	 */
	@GetMapping("/admin")
	public String getAdmin() {
		return "Hello Admin Role";
	}
	
	/**
	 * Either User or Admin
	 * @return
	 */
	@GetMapping("/useroradmin")
	public String getUserOrAdmin() {
		return "Hello User or Admin Role";
	}
	
	@GetMapping("/listTeam")
	public List<String> listTeam() {
		List<String> list = new ArrayList();
		list.add("Team Memeber 1");
		list.add("Team Memeber 2");
		list.add("Team Memeber 3");
		return list;
	}
	
	@GetMapping("/listEmp")
	public List<String> listEmp() {
		List<String> list = new ArrayList();
		list.add("Team Memeber 1");
		list.add("Team Memeber 2");
		list.add("Team Memeber 3");
		list.add("Team Lead 1");
		list.add("Team Lead 2");
		return list;
	}
}

App.java

package com.springSecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class App {
	public static void main(String[] args) {
		ApplicationContext applicationContext = SpringApplication.run(App.class, args);
	}
}

Release.sql

create table emp_temp
(
EMP_ID number(2),
EMP_USERNAME varchar2(10),
EMP_PASS varchar2(10),
EMP_ROLES varchar2(50),
EMP_PERMISSIONS varchar2(50)
);


insert into emp_temp values (1,'Tyson','123','ADMIN','');
insert into emp_temp values (2,'Justin','123','USER','');
insert into emp_temp values (3,'Martin','123','ADMIN','MANAGER');
insert into emp_temp values (4,'Jake'',123','USER','MANAGER');
insert into emp_temp values (5,'Duke','123','ADMIN','LEAD');
insert into emp_temp values (6,'Fade'',123','USER','LEAD');

Screenshots:

Generating JWT
RequestWithJWT

Reference :

Spring Boot Logging using Logback

  • Just place the logback.xml file in the classpath and it will start working

logback.xml (place in resource folder i.e class path)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<include resource="org/springframework/boot/logging/logback/defaults.xml" />
	<property name="LOG_FILE" value="logs/app.log" />

	<appender name="FILE-ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${LOG_FILE}</file>

		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<fileNamePattern>logs/archived/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
			<!-- each archived file, size max 5KB -->
			<maxFileSize>5KB</maxFileSize>
			<!-- total size of all archive files, if total size > 20KB, it will delete 	old archived file -->
			<totalSizeCap>20KB</totalSizeCap>
			<!-- 60 days to keep -->
			<maxHistory>60</maxHistory>
		</rollingPolicy>

		<encoder>
			<pattern>%d %p %c{1.} [%t] %m%n</pattern>
		</encoder>
	</appender>

	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
		<layout class="ch.qos.logback.classic.PatternLayout">
			<Pattern>
				%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
			</Pattern>
		</layout>
	</appender>


	<logger name="com.dailycodebuffer" level="trace" additivity="false">
		<appender-ref ref="FILE-ROLLING" />
	</logger>

	<root level="error">
		<appender-ref ref="FILE-ROLLING" />
	</root>

	<logger name="com.dailycodebuffer" level="debug" additivity="false">
		<appender-ref ref="CONSOLE" />
	</logger>
	<root level="error">
		<appender-ref ref="CONSOLE" />
	</root>

</configuration>

application.properties

server.port=9191
logging.level.org.springframework=DEBUG
#logging.level.SpringSecurity=DEBUG
#logging.level.root=INFO

logging.pattern.console=%d [%level] %c{1.} [%t] %m%n
logging.file = appLog.log
logging.pattern.file=%d [%level] %c{1.} [%t] %m%n

#[%d] date [%level] i.e info trace etc [%c] = class name [%t] = thread  [%m] = message [%n] new line   

Reference :

Spring Security with DB Authentication

Steps :

  1. Create DB Table that will support your authentication
  2. Create User Bean with all the required fields.
  3. Create a @Repository Bean that creates the User Bean using username
  4. Test step 1 and 2 by directly calling the repository via a controller
  5. Create implementation of UserDetails interface (UserPrincipal) using decorator pattern
  6. Create implementation of UserDetailsService interface (UserPrincipalDetailsService)
  7. Hook the new Authentication provider created into the implementation of WebSecurityConfigurerAdapter class
  8. Test the implementation
    • This implementation almost 90% does not work at the first attempt so debug will most effeminately will be required, below points shall help
    • Check if the username is getting correctly passed into loadUserByUsername() method
    • Check base 64 encoding
    • Check the password encoder setting
    • Check the logs
    • Disable the CSRF

Step 1 : Create DB Table that will support your authentication

create table emp_temp
(
EMP_ID number(2),
EMP_USERNAME varchar2(10),
EMP_PASS varchar2(10),
EMP_ROLES varchar2(50),
EMP_PERMISSIONS varchar2(50)
);


insert into emp_temp values (1,'Tyson','123','ADMIN','');
insert into emp_temp values (2,'Justin','123','USER','');
insert into emp_temp values (3,'Martin','123','ADMIN','MANAGER');
insert into emp_temp values (4,'Jake'',123','USER','MANAGER');
insert into emp_temp values (5,'Duke','123','ADMIN','LEAD');
insert into emp_temp values (6,'Fade'',123','USER','LEAD');

Step 2 : Create User Bean with all the required fields.

User.java

package com.springSecurity.user;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class User {
	
	private String id;
	private String username;
	private String password;
	private String roles;
	private String permissions;
	
	public User() {
		
	}
	public User(String id, String username, String password, String roles, String permissions) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.roles = roles;
		this.permissions = permissions;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getRoles() {
		return roles;
	}
	public void setRoles(String roles) {
		this.roles = roles;
	}
	public String getPermissions() {
		return permissions;
	}
	public void setPermissions(String permissions) {
		this.permissions = permissions;
	}
	public List<String> getRolesList(){
		List<String> arrayList = new ArrayList<>(); 
		if(this.roles.length()>0) {
			Collections.addAll(arrayList,this.roles.split(",")); 
		}
		return arrayList;
	}
	
	public List<String> getPermissionsList(){
		List<String> arrayList = new ArrayList<>(); 
		if(this.roles.length()>0) {
			Collections.addAll(arrayList,this.permissions.split(",")); 
		}
		return arrayList;
	}
}

Step 3 : Create a @Repository Bean that creates the User Bean using username

UserDAO.java

package com.springSecurity.user;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAO {
	
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	public User getEmpDetails(String userName) {
		StringBuilder query = new StringBuilder();
		query.append("select EMP_ID, EMP_USERNAME, EMP_PASS, EMP_ROLES, EMP_PERMISSIONS from emp_temp where EMP_USERNAME=?");
		User user =jdbcTemplate.queryForObject(query.toString(),new Object[] {userName}, new RowMapper<User>() {

			@Override
			public User mapRow(ResultSet rs, int rowNum) throws SQLException {
				return new User(
						rs.getString("EMP_ID"),
						rs.getString("EMP_USERNAME"),
						rs.getString("EMP_PASS"),
						rs.getString("EMP_ROLES"),
						rs.getString("EMP_PERMISSIONS"));
			}
			
		});
		return user;
	}
}

Step 4 :Test step 1 and 2 by directly calling the repository via a controller

Controller File

package com.springSecurity;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.springSecurity.user.User;
import com.springSecurity.user.UserService;

@RestController
public class DBController {
	
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	@Autowired
	UserService userService;	

	@PostMapping("/empList")
	public User getEmpList2(@RequestBody Map<String,String> data){
		return userService.getEmpDetails(data.get("loginId"));
	}
}

Service File

package com.springSecurity.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
	
	@Autowired
	UserDAO userDAO;
	
	public User getEmpDetails(String userName) {
		return userDAO.getEmpDetails(userName);
	}
}

Test Using Rest Tool

Step 5 : Create implementation of UserDetails interface (UserPrincipal) using decorator pattern

UserPrincipal.java

package com.springSecurity.user;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class UserPrincipal implements UserDetails{
	
	private static final long serialVersionUID = 1L;

	private User user;
	
	public UserPrincipal(User user) {
		this.user = user;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> authorityList = new ArrayList<>();
		this.user.getPermissionsList().forEach(p -> {
			GrantedAuthority authority = new SimpleGrantedAuthority(p);
			authorityList.add(authority);
		});
		
		this.user.getRolesList().forEach(p -> {
			GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_"+p);
			authorityList.add(authority);
		});
		
		return authorityList;
	}

	@Override
	public String getPassword() {
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		return user.getUsername();
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

}

Step 6 : Create implementation of UserDetailsService interface (UserPrincipalDetailsService)

UserPrincipalDetailsService.java

package com.springSecurity.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserPrincipalDetailsService implements UserDetailsService{

	@Autowired
	UserDAO userDAO;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userDAO.getEmpDetails(username);
		UserPrincipal userPrincipal = new UserPrincipal(user);
		return userPrincipal;
	}
}

Step 7 : Hook the new Authentication provider created into the implementation of WebSecurityConfigurerAdapter class

SecurityConfig.java

package com.springSecurity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.springSecurity.user.UserPrincipalDetailsService;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty (name = "myproject.security.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	@Autowired
	UserPrincipalDetailsService userPrincipalDetailsService;
	
	/**@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth
			.inMemoryAuthentication()
			.withUser("admin1").password(passwordEncoder().encode("admin")).roles("ADMIN").and()
			.withUser("admin2").password(passwordEncoder().encode("admin")).roles("ADMIN").authorities("MANAGER").and()
			.withUser("user1").password(passwordEncoder().encode("user")).roles("USER").and()
			.withUser("user2").password(passwordEncoder().encode("user")).roles("USER").authorities("LEAD");
	}*/
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authenticationProvider());
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http.csrf().disable();
		
		http
			.authorizeRequests()
			.antMatchers("/").permitAll()//bypass authetication and authorization
			.antMatchers("/empList").permitAll()//bypass authetication and authorization
			.antMatchers("/empList2").permitAll()//bypass authetication and authorization
			.antMatchers("/profile").authenticated()//Authentication only required
			.antMatchers("/user/**").hasRole("USER")//Authetication and Authorization required
			.antMatchers("/admin/**").hasRole("ADMIN")//Authetication and Authorization required
			.antMatchers("/useroradmin").hasAnyRole("ADMIN","USER")//Either admin or user role required
			//permissions
			.antMatchers("/listTeam").hasAuthority("LEAD")//Authetication, Authorization (since /user/** is required) and permission required
			.antMatchers("/listEmp").hasAuthority("MANAGER")//Authetication, Authorization and permission required
			.antMatchers("/**").denyAll()
			.and()
			.httpBasic();
		http
			.sessionManagement() 
			.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
	
	@Bean
	DaoAuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
		daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
		daoAuthenticationProvider.setUserDetailsService(userPrincipalDetailsService);
		return daoAuthenticationProvider;
	}
	
	@Bean
	PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
//		return new BCryptPasswordEncoder();
	}
	
}

Step 8 : Test the implementation

Below is the sample controller used for testing

package com.springSecurity;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.security.RolesAllowed;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {
	
	/**
	 * Accessable without Authentication and authorization
	 * @return
	 */
	@GetMapping("/")
	public String getWelcomePage() {
		return "Hello....welcome to our website";
	}
	
	/**
	 * All logged in users
	 * Accessable with authentication but authorization not required
	 * @return
	 */
	@GetMapping("/profile")
	public String getProfile() {
		return "Welcome to profile page";
	}
	
	/**
	 * Only logged in user with role USER can access this
	 * @return
	 */
	@GetMapping("/user")
	public String getUser() {
		return "Hello User Role";
	}
	
	/**
	 * Only loggged in admin can access this
	 * @return
	 */
	@GetMapping("/admin")
	public String getAdmin() {
		return "Hello Admin Role";
	}
	
	/**
	 * Either User or Admin
	 * @return
	 */
	@GetMapping("/useroradmin")
	public String getUserOrAdmin() {
		return "Hello User or Admin Role";
	}
	
	@GetMapping("/listTeam")
	public List<String> listTeam() {
		List<String> list = new ArrayList();
		list.add("Team Memeber 1");
		list.add("Team Memeber 2");
		list.add("Team Memeber 3");
		return list;
	}
	
	@GetMapping("/listEmp")
	public List<String> listEmp() {
		List<String> list = new ArrayList();
		list.add("Team Memeber 1");
		list.add("Team Memeber 2");
		list.add("Team Memeber 3");
		list.add("Team Lead 1");
		list.add("Team Lead 2");
		return list;
	}
}

Get the UserDetails from the Spring Security using below steps :

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

		if (principal instanceof UserDetails) {
		  username = ((UserDetails)principal).getUsername();
		} else {
		  username = principal.toString();
		}

Reference :

Youtube : Part 1 , Part 2

Code :

GitHub : Branch – spring-security-db-authentication

Enable HTTPS/SSL in SpringBoot

Steps to enable HTTPS :

  1. Generate HTTPS Certificate
    1. Create your own certificate from keytool
    2. Buy the certificate
    3. Generate free certificate from OpenSSL
  2. Make changes in application.properties
  3. Add Bean for ServletWebServerFactory

Generate HTTPS Certificate

Generating certificate using keytool

Add bootsecurity.p12 file into your resource folder of springboot

Make changes in application.properties

application.properties


Spring Authority/Permission Based Authorization for URLs

We will continue this with the help of previous code present in this blog

Roles :

  • / – permitted for all
  • /profile – permitted for only logged in user without any role
  • /user – permitted for logged in user with role USER
  • /admin – permitted for logged in user with role ADMIN
  • /useroradmin – permitted for logged in user with role ADMIN and USER

Permissions :

  • /listTeam – Should have USER role and TEAM_LEAD permission
  • /listEmp – Should have ADMIN role and MANAGER permission

App.java

package com.springSecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class App {
	public static void main(String[] args) {
		ApplicationContext applicationContext = SpringApplication.run(App.class, args);
	}
}

SecurityConfig.java

package com.springSecurity;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty (name = "myproject.security.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth
			.inMemoryAuthentication()
			.withUser("admin1").password(passwordEncoder().encode("admin")).roles("ADMIN").and()
			.withUser("admin2").password(passwordEncoder().encode("admin")).roles("ADMIN").authorities("MANAGER").and()
			.withUser("user1").password(passwordEncoder().encode("user")).roles("USER").and()
			.withUser("user2").password(passwordEncoder().encode("user")).roles("USER").authorities("TEAM_LEAD");
//			.withUser("admin").password("admin").roles("ADMIN").and()
//			.withUser("user").password("user").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
			.antMatchers("/").permitAll()//bypass authetication and authorization
			.antMatchers("/profile").authenticated()//Authentication only required
			.antMatchers("/user/**").hasRole("USER")//Authetication and Authorization required
			.antMatchers("/admin/**").hasRole("ADMIN")//Authetication and Authorization required
			.antMatchers("/useroradmin").hasAnyRole("ADMIN","USER")//Either admin or user role required
			//permissions
			.antMatchers("/listTeam").hasAuthority("TEAM_LEAD")//Authetication, Authorization (since /user/** is required) and permission required
			.antMatchers("/listEmp").hasAuthority("MANAGER")//Authetication, Authorization and permission required
			.antMatchers("/**").denyAll()
			.and()
			.httpBasic();
		http
			.sessionManagement() 
			.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
	
	@Bean
	PasswordEncoder passwordEncoder() {
//		return NoOpPasswordEncoder.getInstance();
		return new BCryptPasswordEncoder();
	}
	
}

Controller.java

package com.springSecurity;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.security.RolesAllowed;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {
	
	/**
	 * Accessable without Authentication and authorization
	 * @return
	 */
	@GetMapping("/")
	public String getWelcomePage() {
		return "Hello....welcome to our website";
	}
	
	/**
	 * All logged in users
	 * Accessable with authentication but authorization not required
	 * @return
	 */
	@GetMapping("/profile")
	public String getProfile() {
		return "Welcome to profile page";
	}
	
	/**
	 * Only logged in user with role USER can access this
	 * @return
	 */
	@GetMapping("/user")
	public String getUser() {
		return "Hello User Role";
	}
	
	/**
	 * Only loggged in admin can access this
	 * @return
	 */
	@GetMapping("/admin")
	public String getAdmin() {
		return "Hello Admin Role";
	}
	
	/**
	 * Either User or Admin
	 * @return
	 */
	@GetMapping("/useroradmin")
	public String getUserOrAdmin() {
		return "Hello User or Admin Role";
	}
	
	@GetMapping("/listTeam")
	public List<String> listTeam() {
		List<String> list = new ArrayList();
		list.add("Team Memeber 1");
		list.add("Team Memeber 2");
		list.add("Team Memeber 3");
		return list;
	}
	
	@GetMapping("/listEmp")
	public List<String> listEmp() {
		List<String> list = new ArrayList();
		list.add("Team Memeber 1");
		list.add("Team Memeber 2");
		list.add("Team Memeber 3");
		list.add("Team Lead 1");
		list.add("Team Lead 2");
		return list;
	}
}

Based64 encoded username:password i.e admin1:admin is YWRtaW4xOmFkbWlu

Making the API call :

Spring Role Based Authorization for URLs

In Below example

  • / – permitted for all
  • /profile – permitted for only logged in user without any role
  • /user – permitted for logged in user with role USER
  • /admin – permitted for logged in user with role ADMIN
  • /useroradmin – permitted for logged in user with role ADMIN and USER

App.java

package com.springSecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class App {
	public static void main(String[] args) {
		ApplicationContext applicationContext = SpringApplication.run(App.class, args);
	}
}

SecurityConfig.java

package com.springSecurity;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty (name = "myproject.security.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth
			.inMemoryAuthentication()
			.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN")
//			.withUser("admin").password("admin").roles("ADMIN")
			.and()
			.withUser("user").password(passwordEncoder().encode("user")).roles("USER");
//			.withUser("user").password("user").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
			.antMatchers("/").permitAll()//bypass authetication and authorization
			.antMatchers("/profile").authenticated()//Authentication only required
			.antMatchers("/user").hasRole("USER")//Authetication and Authorization required
			.antMatchers("/admin").hasRole("ADMIN")//Authetication and Authorization required
			.antMatchers("/useroradmin").hasAnyRole("ADMIN","USER")//Either admin or user role required
			.and()
			.httpBasic();
		http
			.sessionManagement() 
			.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
	
	@Bean
	PasswordEncoder passwordEncoder() {
//		return NoOpPasswordEncoder.getInstance();
		return new BCryptPasswordEncoder();
	}
	
}

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.0.5.RELEASE'
    id 'io.spring.dependency-management' version '1.0.7.RELEASE'
}

repositories {
    jcenter()
}

dependencies {
    implementation 'com.google.guava:guava:28.0-jre'
    testImplementation 'junit:junit:4.12'
    implementation 'org.springframework.boot:spring-boot-dependencies:2.0.5.RELEASE'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

	//Oracle Integration
	implementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0'
	implementation 'org.springframework:spring-jdbc:3.2.0.RELEASE'
	
	//Spring Security
	implementation 'org.springframework.boot:spring-boot-starter-security:2.4.4'
	
	
	
    components {
        withModule('org.springframework:spring-beans') {
            allVariants {
                withDependencyConstraints {
                    it.findAll { it.name == 'snakeyaml' }.each { it.version { strictly '1.19' } }
                }
            }
        }
    }
}

bootJar {
    mainClassName = 'SpringSecurity.App'
}

application.properties

server.port=9191
logging.level.org.springframework=DEBUG

#security.ignored=/**
myproject.security.enabled=true

Reference :

Code :

  • Git Hub – branch : spring-security-authorization

HTTP Headers : Spring Basic Authentication via SoapUI

The rest endpoint developed in this blog is being called via SoapUI tool in this blog

Steps :

  • Note the name of the endpoint you want to call, example : http://localhost:9191/admin
  • Since we need to do a authentication we need to know the username and password in out case username is admin and password is admin
  • Now its base64 encode the credenticals
    • Arrange the credentials like username:password example admin:admin
    • encrypt the credentials using base64 encoder online
    • after encryption the value should look like YWRtaW46YWRtaW4=
    • Now add this value in header in this way
      Authorization : YWRtaW46YWRtaW4=
  • Check the method if it is get or post correctly

Example :

Tips :

  • Http error code is 401 then it is Authentication Failure
  • Http error code is 404 then it is page/resource not found
  • Check if the endpoint is a get or post
  • Check the if the header Authorization : Basic YWRtaW46YWRtaW4= is set correctly, decrypt YWRtaW46YWRtaW4= using base64 decoder and check
  • Check the console log of SpringBoot to check at which step of spring security it is failing

Spring Basic Authentication with BCryptPasswordEncoder

App.java

package com.springSecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class}) //disabling auto configuration
public class App {
	public static void main(String[] args) {
		ApplicationContext applicationContext = SpringApplication.run(App.class, args);
	}
}

SecurityConfig.java

package com.springSecurity;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty (name = "myproject.security.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth
			.inMemoryAuthentication()
			.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN")
//			.withUser("admin").password("admin").roles("ADMIN")
			.and()
			.withUser("user").password(passwordEncoder().encode("user")).roles("USER");
//			.withUser("user").password("user").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
			.anyRequest().authenticated()
			.and()
			.httpBasic();
	}
	
	@Bean
	PasswordEncoder passwordEncoder() {
//		return NoOpPasswordEncoder.getInstance();
		return new BCryptPasswordEncoder();
	}
	
}

application.properties

server.port=9191
logging.level.org.springframework=DEBUG

#Spring Secuity
spring.security.user.name=tyson
spring.security.user.password=tyson

#security.ignored=/**
myproject.security.enabled=true


#below does not work
#spring.security.enabled=false
#security.basic.enabled=false
#security.ignored=/**
#management.security.enabled=false
#spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.0.5.RELEASE'
    id 'io.spring.dependency-management' version '1.0.7.RELEASE'
}

repositories {
    jcenter()
}

dependencies {
    implementation 'com.google.guava:guava:28.0-jre'
    testImplementation 'junit:junit:4.12'
    implementation 'org.springframework.boot:spring-boot-dependencies:2.0.5.RELEASE'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

	//Oracle Integration
	implementation 'com.oracle.ojdbc:ojdbc8:19.3.0.0'
	implementation 'org.springframework:spring-jdbc:3.2.0.RELEASE'
	
	//Spring Security
	implementation 'org.springframework.boot:spring-boot-starter-security:2.4.4'
	
	
	
    components {
        withModule('org.springframework:spring-beans') {
            allVariants {
                withDependencyConstraints {
                    it.findAll { it.name == 'snakeyaml' }.each { it.version { strictly '1.19' } }
                }
            }
        }
    }
}

bootJar {
    mainClassName = 'SpringSecurity.App'
}

Disabling Spring Security

application.properties

security.ignored=/**
myproject.security.enabled=true

Disabling Auto Spring Security Confirg

App.java

@SpringBootApplication
(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})

Reference :

Code

  • Github : branch – spring-secuity-with-basic-inmemory-authentication

Export and Import Module in Node Js

  • Export a function in another file
  • We can achieve export by using the object called module
  • The object module has a property called exports which contains what you want to export out of the module

Exporting Single Functions

add.js

function add(a,b){
    return a+b;
}

module.exports = add;

first.js

var addFn = require('./add.js');
function greet(name){
    console.log("Hello there "+name)
}
greet("Tyson");
console.log(addFn(10,20));

Output :

Exporting Multiple Functions

Version 1 : Quick method

add.js

function add(a,b){
    return a+b;
}

function sub(a,b){
    return a-b;
}

module.exports = {
    add,
    sub
};

/* Note above is the shorthand for 
module.exports = {
    addFunction : add,
    subFunction : sub
};
 */

first.js

var operatorsObject = require('./add.js');
function greet(name){
    console.log("Hello there "+name)
}
greet("Tyson");
console.log(operatorsObject.add(10,20));
console.log(operatorsObject.sub(10,20));

Version 2 : Recommended method

add.js

function add(a,b){
    return a+b;
}

function sub(a,b){
    return a-b;
}

module.exports.add = add;
module.exports.sub = sub;

first.js

var operatorsObject = require('./add.js');
function greet(name){
    console.log("Hello there "+name)
}
greet("Tyson");
console.log(operatorsObject.add(10,20));
console.log(operatorsObject.sub(10,20));

Version 3 : Recommended method with a shorthand

  • module.exports was added the above example.
  • but we can shorthand it to export only
  • it works because node js has some declaration at some level something like
    var exports = module.exports

add.js

function add(a,b){
    return a+b;
}

function sub(a,b){
    return a-b;
}

exports.add = add;
exports.sub = sub;

first.js

var operatorsObject = require('./add.js');
function greet(name){
    console.log("Hello there "+name)
}
greet("Tyson");
console.log(operatorsObject.add(10,20));
console.log(operatorsObject.sub(10,20));
This image has an empty alt attribute; its file name is image-3.png