This article mainly translates the official documentation of Spring, mainly introduces the basic concepts and basic interfaces and principles of SS.

Two core concepts in Spring Security: Authentication; Authorization; Spring Security can be used to implement authentication and authorization between systems such as OAuth, OpenID, LDAP, CAS, JAAS, etc. For example, we can use Spring Security and OAuth to achieve single sign-on.

The Authentication validation

Authentication The main interface is AuthenticationManager

public interface AuthenticationManager {
  Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
Copy the code

There are three cases of the authenticate() method call:

  1. If the Authentication succeeds, an Authentication instance is returned
  2. If authentication fails, an AuthenticationException is thrown
  3. Undecided, the most common AuthenticationManager interface implementation class that returns NULL is ProviderManager, which internally delegates validation to a series of AuthenticationProviders. The AuthenticationProvider interface is a bit like the AuthenticationManager interface, but it has an additional method that allows the caller to determine whether the given Authentication instance type is supported or not. This method is the supports method below.
public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
	boolean supports(Class
        authentication);
}
Copy the code

ProviderManager delegates authentication to a list of AuthenticationProviders, which is actually a list of AuthenticationProviders, so it can support multiple authentication policies in an application. During Authentication, all authenticationProviders are authenticated one by one. The supports method is called to check whether this type of Authentication is authenticated. If yes, Authentication is performed; otherwise, the Authentication is skipped. If all authenticationProviders cannot decide that null is returned, the parent’s AuthenticationManager validation is called (if the parent is not empty). Sometimes there are logical groups of authentications in an application, each with its own AuthenticationManager (usually the ProviderManager), and these groups share a parent AuthenticationManager.

Customize the AuthenticationManager

SpringSecurity provides some configuration help class to go and get the AuthenticationManager functions, the most commonly used is AuthenticationManagerBuilder, It can configure User Details in in-memory, JDBC, LDAP, or add a custom UserDetailService. eg.

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
  @AutowiredDataSource dataSource; .// web stuff here
  @Override
  public configure(AuthenticationManagerBuilder builder) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave").password("secret").roles("USER"); }}Copy the code

????? The configure method does not return a type

Authorization Authorization

The main interface of Authorization is AccessDecisionManager. The framework provides three implementation classes: Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday, Thursday This is a bit like providerManager-like authentication delegating to the AuthenticationProvider. Here an AccessDecisionVoter is an abstract voter.

public interface AccessDecisionVoter<S> {
    boolean supports(ConfigAttribute attribute);    
    boolean supports(Class
        clazz);
    int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
}
Copy the code

An AccessDecisionVoter considers authenticating the principal Authentication and a security Object modified by ConfigAttributes. Object is completely universal in the AccessDecisionManager and AccessDecisionVoter signatures, and Object represents everything that the user might want to access (usually network resources or Java class method calls). ConfigAttributes is also generic, representing modifications to a security object with metadata that determines the level of permission required to access the security object. ConfigAttribute is an interface that has only one very generic method and returns a String, so these strings somehow encode the intent of the resource owner, expressing rules about who is allowed access to the secure object. A typical ConfigAttribute is the user’s role name (such as ROLE_ADMIN or ROLE_AUDIT), and they usually have a special format (such as ROLE_ prefix) or an expression to indicate the need for evaluation. Most people just use the default implementation of the AccessDecisionManager class AffirmativeBased (it is allowed to pass if there is no opposition). Any customization will happen to the voter, either by adding a new one or modifying the existing way of working. It is very common to use ConfigAttribute with EL expressions, such as isFullyAuthenticated() && hasRole(‘FOO’), which is supported by an AccessDecisionVoter that processes expressions and creates context for them. To extend the range of processing expression, need custom SecurityExpressionRoot, SecurityExpressionHandler.

Web Security

The use of Spring Security at the Web layer is based on Servlet filters, as shown below in a typical layering of a single HTTP request handler.

Create custom filter chains

The default backoff filter chain (match /**) in Spring Boot applications has a pre-defined order (securityProperties.basic_auth_order), which you can turn off with security.basic.enabled=false, Or you can use it as a fallback filter where you only define lower order filters that match other rules. To do this, you just need to add a WebSecurityConfigurerAdapter @ Bean (or WebSecurityConfigurer) type, and use @ Order comment to modify this Class. Such as:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/foo/**")... ; }}Copy the code

The scheduling and authorization requests match

The security filter chain (or equivalent WebSecurityConfigurerAdapter) there is a request verifier, used to determine whether applied to an HTTP request. Once the decision is made to apply a particular filter chain, no other filter chain is applied. But in the filter chain, you can have more fine-grained control over authorization by setting additional matchers in the HttpSecurity configurator. Example:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/foo/**")
        .authorizeRequests()
        .antMatchers("/foo/bar").hasRole("BAR")
        .antMatchers("/foo/spam").hasRole("SPAM") .anyRequest().isAuthenticated(); }}Copy the code

One of the most common mistakes to make in configuring Spring Security is to forget that these matchers apply to different processes, one to request matchers for the entire filter chain, and the other to simply select the access rules to apply.

Combining application security rules with Actuator rules involves Spring Boot Actuator content, which will be skipped for the moment

Methods the safety

In addition to supporting Web applications, Spring Security also supports the application of access rules to Java method execution. This is a different type of resource protection to Sping Security. For users, this means using ConfigAttribute strings (such as roles or expressions) in the same format to declare access rules, but in different places in the code. The first step is to make the method work safely, for example:

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {}Copy the code

We can then modify the method resources directly, for example:

@Service
public class MyService {
  @Secured("ROLE_USER")
  public String secure(a) {
    return "Hello Security"; }}Copy the code

This example is a service with secure methods. If Spring creates an @bean of this type, it will be proxied and the caller will have to go through a security interceptor before the method can actually execute. If access is denied, the caller gets an AccessDeniedException exception instead of the actual method result. There are other annotations that can be used to enforce security constraints on methods, particularly @preauthorize and @postauthorize, which allow you to write expressions containing references to method parameters and return values, respectively.

Working with Threads

Spring Security is essentially thread-bound because it needs to make the currently authenticated principal available to a variety of downstream consumers. The basic building block is SecurityContext, which may include Authentication(when a user logs in, It will be an authenticated Authentication). You can always access and manipulate the SecurityContext through static convenience methods in the SecurityContextHolder. Such as:

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
assert(authentication.isAuthenticated);
Copy the code

If you need to access a currently authenticated user in a network endpoint, you can use a method parameter in @requestMapping, for example:

@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {...// do stuff with user
}
Copy the code

This annotation will generate method parameters when Authentication is pulled out of the SecurityContext and the getPrincipal() method is called on it. The type of Principal in Authentication depends on the AuthenticationManager used to authenticate the Authentication, so this can be a useful tip for getting type-safe references to user data. If Spring Security is using the Principal from HttpServletRequest, it will be of type Authentication, so you can use it directly:

@RequestMapping("/foo")
public String foo(Principal principal) { Authentication authentication = (Authentication) principal; User = (User) authentication.getPrincipal(); .// do stuff with user
}
Copy the code

Processing Secure Methods Asynchronously

Since SecurityContext is thread-bound, if you want to perform any background processing that calls a secure method, such as using @Async, you need to ensure that the context is propagated. This boils down to wrapping the SecurityContext with tasks (Runnable, Callable, and so on) that are executed in the background. Spring Security provides some helpers to simplify this process, such as wrappers for Runnable and Callable. To propagate SecurityContext to the @Async method, you need to provide an AsyncConfigurer and ensure that Executor is of the correct type:

@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {
  @Override
  public Executor getAsyncExecutor(a) {
    return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5)); }}Copy the code