Introduction
Application security includes “authentication” and “authorization”:
- “Authentication” is the process of establishing a principal is who they claim to be(a “principal” generally means a user, device or some other system which can perform an action in your application).
- “Authorization” refers to the process of deciding whether a principal is allowed to perform an action within your application.
Spring Security provides a deep set of authorization capabilities, they are divided into mainly three areas:
- authorizing web requests
- authorizing whether methods can be invoked
- authorizing access to individual domain object instances
maven dependencies:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
The major building blocks of Spring Security:
SecurityContextHolder
, to provide access toSecurityContext
.SecurityContext
, to hold theAuthentication
and possibly request-specific security imformation.Authentication
, to represent the principal in a Spring Security-specific manner.GrantedAuthority
, to reflect the application-wide permissions granted to a principal.UserDetails
, to provide the necessary information to build an Authentication object from your application’s DAOs or other source of security data.UserDetailsService
, to create aUserDetails
when passed in aString
-based username( or certificate ID or the like).
The authentication process within Spring Security:
- The username and passwod are obtained and combined into an instance of
UsernamePasswordAuthenticationToken
(an instance ofAuthentication
interface) - The token is passed to an instance of
AuthenticationManager
for validation. - The
AuthenticationManager
returns a fully populatedAuthentication
instance on successful authentication. - The security context is established by calling
SecurityContextHolder.getContext().setAuthentication(...)
, passing in the authentication object.
Integrate with Spring MVC
Refister the springSecurityFilterChain Filter for every URL in application. After that we would ensure that WebSecurityConfig
was loaded in out existing ApplicationInitializer.
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
// ... other overrides ...
}
HttpSecurity
Implement interface WebSecurityConfigurerAdapter
and override the config
method like what below do:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() 1
.antMatchers("/resources/**", "/signup", "/about").permitAll() 2
.antMatchers("/admin/**").hasRole("ADMIN") 3
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") 4
.anyRequest().authenticated() 5
.and()
// ...
.formLogin();
}
If you do not specify a login page, Spring Security will generate one automatically.
Handling Logouts
Spring Security logout progress:
- Invalidating the HTTP Session
- Cleaning up any RememberMe authentication that was configured
- Clearing the SecurityContextHolder
- Redirect to /login?logout
You can custom logout action by the config method as well.
LogoutHandler
Generally, LogoutHandler implementations indicate classes that are able to participate in logout handling. They are expected to be invoked to perform necessary clean-up. As such they should not throw exceptions. Various implementations are provided:
- PersistentTokenBasedRememberMeServices
- TokenBasedRememberMeServices
- CookieClearingLogoutHandler
- CsrfLogoutHandler
- SecurityContextLogoutHandler
LogoutSuccessHandler
Called after a successful logout by the LogoutFilter, to handle e.g redirection or forwarding to the appropriate destination.
implementations:
- SimpleUrlLogoutSuccessHandler
- HttpStatusReturningLogoutSuccessHandler
Authentication
JDBC Authentication
Java config example as below:
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser(users.username("user").password("password").roles("USER"))
.withUser(users.username("admin").password("password").roles("USER","ADMIN"));
}
Custom Authentication
- AuthenticationProvider
You can define custom authentication by exposing a custom AuthenticationProvider as a bean.For example, the following will customize authentication assuming that SpringAuthenticationProvider implements AuthenticationProvider:@Bean public SpringAuthenticationProvider springAuthenticationProvider() { return new SpringAuthenticationProvider(); }
- UserDetailsService
You can define custom authentication by exposing a custom UserDetailsService as a bean. For example, the following will customize authentication assuming that SpringDataUserDetailsService implements UserDetailsService:
You can also customize how passwords are encoded by exposing a PasswordEncoder as a bean. For example, if you use bcrypt you can add a bean definition as shown below:@Bean public SpringDataUserDetailsService springDataUserDetailsService() { return new SpringDataUserDetailsService(); }
@Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
Multiple HttpSecurity
We can configure multiple HttpSecurity instances just as we can have multiple blocks. The key is to extend the WebSecurityConfigurationAdapter multiple times.
@EnableWebSecurity
public class MultiHttpSecurityConfig {
@Bean
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("password").roles("USER").build());
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
return manager;
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
@Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
Method Security
From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. It provides support for JSR-250 annotation security as well as the framework’s original @Secured annotation. From 3.0 you can also make use of new expression-based annotations.
EnableGlobalMethodSecurity
We can enable annotation-based security using the @EnableGlobalMethodSecurity annotation on any @Configuration instance. For example, the following would enable Spring Security’s @Secured annotation.
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly. Spring Security’s native annotation support defines a set of attributes for the method. These will be passed to the AccessDecisionManager for it to make the actual decision:
public interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
Support for JSR-250 annotations can be enabled using
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
}
These are standards-based and allow simple role-based constraints to be applied but do not have the power Spring Security’s native annotations. To use the new expression-based syntax, you would use
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
}
the java code would be
public interface BankService {
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
full list:
@PreAuthorize
@PostAuthorize
@PostFilter- filter the result
@PreFilter:filter the param