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.
|