Java Spring Security

Spring Security is a separate module of the Spring framework that focuses on providing authentication and authorization methods in Java applications. It also takes care of most of the common security vulnerabilities such as CSRF attacks.
To use Spring Security in web applications, we can get started with the simple annotation @EnableWebSecurity.
Feature Name Description
Enabling Spring Security
With this - all requests will be protected with basic auth, username user/generated password (in logs)
For SpringBoot - add dependency
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
Configure Spring Security
with adding this config - simple login page added
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
Types of User Store
  • An in-memory user store
  • A JDBC-based user store
  • An LDAP-backed user store
  • A custom user details service
In-Memory user store
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 auth.inMemoryAuthentication().withUser("buzz").password("infinity").authorities("ROLE_USER")
 .and()
 .withUser("woody").password("bullseye").authorities("ROLE_USER");
}
JDBC-based user store

Should be Tables: users, authorities, groups with certain fields
@Autowired
DataSource dataSource;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 auth.jdbcAuthentication().dataSource(dataSource);
 // OR queries could be overridden
 auth.jdbcAuthentication().dataSource(dataSource)
 .usersByUsernameQuery("select username, password, enabled from Users " + "where username=?")
 .authoritiesByUsernameQuery("select username, authority from UserAuthorities " + "where username=?");
}
Working With Encoded Passwords
Add passwordEncoder to config.
auth.jdbcAuthentication().dataSource(dataSource)
 ...
 .passwordEncoder(new StandardPasswordEncoder("53cr3t"));
The method accepts other implementations of encoders like: BCryptPasswordEncoder, NoOpPasswordEncoder (no encoding), Pbkdf2PasswordEncoder, SCryptPasswordEncoder, StandardPasswordEncoder (SHA-256 hashing encryption)
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
LDAP-backed user store
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 auth.ldapAuthentication()
 .userSearchBase("ou=people")
 .userSearchFilter("(uid={0})")
 .groupSearchBase("ou=groups")
 .groupSearchFilter("member={0}")
 .passwordCompare()  // if needed. by default - bind user?
 .passwordEncoder(new BCryptPasswordEncoder())
 .passwordAttribute("passcode")  // if not default
 .contextSource()
 .url("ldap://tacocloud.com:389/dc=tacocloud,dc=com");  // by default localhost:389
	// OR local embedded LDAP 
  .contextSource()
 .root("dc=tacocloud,dc=com")
 .ldif("classpath:users.ldif"); // to provide initial ldif 
}
Custom store
Can use JPA and create UserDetails, UserRepository, UserDetailsService etc.
Then configure
 auth
 .userDetailsService(userDetailsService)
 .passwordEncoder(encoder());
Securing requests

Configure auth and role level for certain requests. A lot of other methods like hasAuthority, hasAnyRole, hasAnyAuthority etc.
Next step to add new configuration
@Override
protected void configure(HttpSecurity http) throws Exception {
 http
 .authorizeRequests()
 .antMatchers("/design", "/orders")
 .hasRole("ROLE_USER")
 .antMatchers(“/”, "/**").permitAll();
}
Custom login page and logout methods.
Preventing cross-site request forgery CSRF. Spring Security has built-in CSRF protection enabled by default.
  • any forms your application submits should include a field named _csrf
  • could be added in ThymeLeaf as <input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
  • sometimes could be added automatically
Get user Info Information about the authenticated user can be obtained via the SecurityContext object (returned from SecurityContextHolder.getContext()) or injected into controllers using @AuthenticationPrincipal.

API-Key

API-Key easy lazy way to protect your service.
  • Key is issued for the user (ususally - service user)
  • Stored in table with user data
  • given to the user
  • Do not secure protect

JWT

JWT structure
information in the payload of the JWT is visible to everyone. So we should not pass any sensitive information like passwords in the payload. Details
Spring Security and JWT Configuration
Example
  • Generating JWT - Expose a POST API with mapping /authenticate. On passing correct username and password it will generate a JSON Web Token(JWT)
  • Validating JWT - If user tries to access GET API with mapping /hello. It will allow access only if request has a valid JSON Web Token(JWT)
Dependency
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
			
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>			
Classes
  • JwtTokenUtil - utility, getUsernameFromToken, getExpirationDateFromToken, getClaimFromToken, generateToken, validateToken. Use io.jsonwebtoken.Jwts
  • JWTUserDetailsService - implements the Spring Security UserDetailsService interface. loadUserByUsername (in-memory store)
  • JwtAuthenticationController - Expose a POST API /authenticate using. The POST API gets username and password in the body- Using Spring Authentication Manager we authenticate the username and password.If the credentials are valid, a JWT token is created using the JWTTokenUtil and provided to the client.
  • JwtRequest - for storing the username and password we recieve from the client.
  • JwtResponse - for creating a response containing the JWT to be returned to the user.
  • JwtRequestFilter - extends the Spring Web Filter OncePerRequestFilter class. For any incoming request this Filter class gets executed. It checks if the request has a valid JWT token. If it has a valid JWT Token then it sets the Authentication in the context, to specify that the current user is authenticated.
  • JwtAuthenticationEntryPoint - extend Spring's AuthenticationEntryPoint class and override its method commence. It rejects every unauthenticated request and send error code 401
  • WebSecurityConfig - config WebSecurityConfig
    @Override
    	protected void configure(HttpSecurity httpSecurity) throws Exception {
    		// We don't need CSRF for this example
    		httpSecurity.csrf().disable()
    				// dont authenticate this particular request
    				.authorizeRequests().antMatchers("/authenticate").permitAll().
    				// all other requests need to be authenticated
    				anyRequest().authenticated().and().
    				// make sure we use stateless session; session won't be used to
    				// store user's state.
    				exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
    				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
    		// Add a filter to validate the tokens with every request
    		httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    	}
    
Classes Open Collapsible
Get user Info Information about the authenticated user can be obtained via the SecurityContext object (returned from SecurityContextHolder.getContext()) or injected into controllers using @AuthenticationPrincipal.