highly customizable authentication and authorization framework.
Browser -> Security interceptor -> spring controller
BasicAuthenticationFilter
Method: doFilterInternal()
ProviderManager
Method: authenticate()
ProviderManager
) has list of AuthenticationProvider
instances.AuthenticationProvider
supports current Authentication
obj.1. JwtAuthenticationProvider - Web token-based authentication.
2. PreAuthenticatedAuthenticationProvider - Supports external identity providers such as OAuth2 or SAML for Single Sign-On (SSO).
3. DaoAuthenticationProvider - Uses `UserDetailsService` to retrieve user details.
4. OAuth2AuthenticationProvider - Handles authentication using OAuth2 tokens.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated().and()
.formLogin().permitAll().and()
.logout().permitAll();
return http.build();
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/custom-login").permitAll().and()
.logout()
.logoutUrl("/custom-logout")
.logoutSuccessUrl("/custom-login?custom-logout=true")
.deleteCookies("JSESSIONID").invalidateHttpSession(true)
.permitAll();
return http.build();
}
}
username is
user
and password comes in console
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// refer login logout page topic above
}
@Bean
public UserDetailsService userDetailsService() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
UserDetails user = User.builder()
.username("srimuthurajesh@gmail.com")
.password(passwordEncoder().encode("rajesh123"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
}
@Entity
public class User implements UserDetails {
@Id
private Long id;
private String username;
private String password;
private String roles;
// Getters and setters
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (roles == null || roles.isEmpty())
return Arrays.asList();
return Arrays.stream(roles.split(",")).map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public boolean isAccountNonExpired() { return true; }
@Override
public boolean isAccountNonLocked() { return true; }
@Override
public boolean isCredentialsNonExpired() { return true; }
@Override
public boolean isEnabled() { return true; }
}
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username); //write findByUsername method inside jpa interface
if (user == null)
throw new UsernameNotFoundException("User not found");
return user;
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
public SecurityConfig(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// refer login logout page topic above }
@Bean
public UserDetailsService userDetailsService() { return customUserDetailsService; }
//we can skip below bean, it will then use NoOpPasswordEncoder.getInstance();
@Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Bean
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
}
<form action="/login" method="post">
public class BranchLocationAuthenticationProvider implements AuthenticationProvider {
private final UserDetailsService userDetailsService;
public BranchLocationAuthenticationProvider(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String branchLocation = (String) authentication.getCredentials();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails == null || !((User) userDetails).getBranchLocation().equals(branchLocation))
throw new BadCredentialsException("Invalid branch location");
return new UsernamePasswordAuthenticationToken(userDetails, branchLocation, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
public SecurityConfig(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// refer login logout page topic above }
@Bean
public BranchLocationAuthenticationProvider branchLocationAuthenticationProvider() {
return new BranchLocationAuthenticationProvider(customUserDetailsService);
}
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = new AuthenticationManagerBuilder(http.getSharedObject(AuthenticationConfiguration.class).getAuthenticationManager());
authenticationManagerBuilder.authenticationProvider(branchLocationAuthenticationProvider());
return authenticationManagerBuilder;
}
}
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email); // Load user by email
if (user == null)
throw new UsernameNotFoundException("User not found with email: " + email);
return user;
}
}
add @EnableOAuth2Sso in main method
add spring configuration
okta.oauth2.issuer= https://dev-165093.okta.com/oauth2/default
okta.oauth2.clientId=0oaz16emnjw4TZVZ0356
okta.oauth2.clientSecret=zEeuINnfu36oNGCWTdmnadAjgT-BtbTu79XdFwe0
spring.main.allow-bean-definition-overriding=true
spring:
cloud:
azure:
active-directory:
enabled: true
profile:
tenant-id: <YOUR TENANT ID>
credential:
client-id: <YOUR CLIENT ID>
client-secret: <YOUR CLIENT SECRET>
server:
port: 9191
forward-headers-strategy: native
Json web token
Format Structure: header.payload.signature
@Service
public class JwtUtil {
private String secret = "javatechie";
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder().setClaims(claims).setSubject(username).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, secret).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/authenticate")
.permitAll().anyRequest().authenticated();
}
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private CustomUserDetailsService service;
@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 = service.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);
}
}
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/authenticate")
.permitAll().anyRequest().authenticated()
.and().exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}