当前位置:Java -> Spring安全认证

Spring安全认证

认证

对安全域的访问必须经过认证过程。认证验证请求访问特定资源的人员的身份,基于此进行授权。例如,认证用户的常见方式是使用用户名和密码。

Spring安全

Spring是一种最受欢迎的开源Java框架之一,已经发展成一个涵盖企业应用开发多个方面的庞大生态系统。Spring安全是其中之一,其专注于认证和访问控制机制。它内置了对用户进行认证的支持。它是保护基于Spring的应用程序的事实标准。

Spring安全提供了对认证和授权的支持。认证验证用户的身份,授权则规定了对资源的访问规则。虽然在大多数情况下默认的认证和授权支持是不错的,但Spring安全的真正威力在于其可定制性。它可以根据客户的要求进行扩展。认证支持适用于servlet和WebFlux环境。

Spring安全可配置以防范诸如会话固化、点击劫持、跨站请求伪造(CSRF)、暴力破解、中间人攻击、跨站脚本攻击(XSS)等攻击。它可以很好地集成Servlet API和Spring Web MVC。

为什么选择Spring安全?

Spring的生态系统为开发小型到大型企业应用提供了全面的编程和配置模型。安全是任何商业应用的不可或缺的部分。

  • 它必须是安全的,对于Spring本身提供必要的API,还有什么更好的框架可供选择?相关:使用Spring安全和OAuth保护Spring REST
  • 通过使用Spring安全,我们实际上是将确定架构和核心安全功能的责任委托给了一组专家团队。
  • Spring安全自诞生以来已经发展,并且在持续发展中,经受住了时间的考验。
  • 因此,在构建Spring应用程序时,包含Spring安全框架是一个好主意。它们不仅能很好地集成,而且也很可靠。

本文将深入探讨Spring安全的技术能力,特别是认证。要查看本文的完整代码,请访问此GitHub仓库

阅读DZone的指南:使用Spring Boot实现OAuth 2.0

下图显示了Spring安全用于满足核心安全需求的基本流程。该图是通用的,并可用于解释框架支持的所有各种认证方法:

Spring安全框架

Spring安全有一系列servlet过滤器(一个过滤器链)。当请求到达服务器时,会被这一系列过滤器拦截(上文图中的第1步)。在响应式世界中,使用新的Spring WebFlux web应用程序框架,编写的过滤器与传统过滤器有很大不同,比如Spring MVC web应用程序框架中使用的过滤器。话虽如此,对于两者来说,基本机制都是相同的。

servlet过滤器代码在过滤器链中的执行会一直跳过,直到到达正确的过滤器。一旦到达基于认证机制使用的正确身份验证过滤器,它会从调用者中提取提供的凭据,最常用的是用户名和密码。

使用提供的值(这里有用户名和密码),该过滤器UsernamePasswordAuthenticationFilter创建一个Authentication对象。在上图中,第2步中使用提供的用户名和密码创建UsernamePasswordAuthenticationToken。步骤2中创建的Authentication对象然后用于调用AuthenticationManager接口的authenticate方法:

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication) throwsAuthenticationException;
}


实际的实现由ProviderManager提供,它拥有已配置的AuthenticationProvider列表。

public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication) throwsAuthenticationException;
    boolean supports(Class<?> authentication);
}


请求通过各种提供程序,并最终尝试对请求进行认证。Spring安全有几个AuthenticationProvider接口。

在上图中,AuthenticationProvider接口需要用户详情(一些提供程序需要这个,但也有一些不需要的),这些详情是由UserDetailsService提供的:

public interface UserDetailsService {
    UserDetailsloadUserByUsername(String username) throws UsernameNotFoundException;
}


UserDetailsService retrieves UserDetails and implements the user interface using the supplied username.

If all goes well, Spring Security creates a fully populated authentication object (authenticate: true, granted authority list, and username), which will contain various necessary details. The authentication object is stored in the SecurityContext object by the filter for future use.

  • An authentication object with authenticated=true if Spring Security can validate the supplied user credentials
  • An AuthenticationException if Spring Security finds that the supplied user credentials are invalid
  •  null if Spring Security cannot decide whether it is true or false (confused state)

Setting Up the Authentication Manager

There are a number of built-inAuthenticationManagermethods in Spring Security that can be easily used in your application. Spring Security also has a number of helper classes, which you can set up using AuthenticationManager. One helper class is theAuthenticationManagerBuilder.

Using this class, it's quite easy to set up the UserDetailsService against a database, in memory, in LDAP, and so on. If the need arises, you could also have your own custom  UserDetailsService (maybe a custom single sign-on solution is already there in your organization).

You can make anAuthenticationManager global, so it will be accessible by your entire application. It will be available for method security and other WebSecurityConfigurerAdapter instances.

WebSecurityConfigurerAdapter is a class that is extended by your Spring configuration file, making it quite easy to bring Spring Security into your Spring application. This is how you set up a global  AuthenticationManager using the @Autowired annotation:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    public void confGlobalAuthManager(AuthenticationManagerBuilderauth) throws Exception {
        auth
            .inMemoryAuthentication()
             .withUser("admin").password("admin@password").roles("ROLE_ADMIN");
    }
}


You can also create local the  AuthenticationManager, which is only available for this particular WebSecurityConfigurerAdapter by overriding the configure method, as shown in the following code:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilderauth) throws Exception {
        auth
            .inMemoryAuthentication()
            .withUser("admin").password("admin@password").roles("ROLE_ADMIN");
}


Another option is to expose the  AuthenticationManager bean by overriding the  authenticationManagerBean method:

@Override
public AuthenticationManagerauthenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}


You can also expose various  AuthenticationManager, AuthenticationProvider, or the UserDetailsService as beans, which will override the default ones.

The preceding code example has used AuthenticationManagerBuilder to configure in-memory authentication.

AuthenticationProvider

AuthenticationProvider provides a mechanism for getting the user details with which authentication can be performed. Spring Security provides a number of AuthenticationProvider implementations, as shown in the following diagram:

Authentication manager provider implementations

Custom AuthenticationProvider

You can also write custom supports like the  AuthenticationProvider by implementing the AuthenticationProvider interface. You have to implement two methods, namely, authenticate (authentication) and  Class<  ?  >aClass:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      String username = authentication.getName();
      String password = authentication.getCredentials().toString();

      if ("user".equals(username) && "password".equals(password)) {
            return new UsernamePasswordAuthenticationToken(username, password, Collections.emptyList());
       } else {
            throw new BadCredentialsException("Authentication failed");
       }
    }

    @Override
    public boolean supports(Class<?>aClass) {
        return aClass.equals(UsernamePasswordAuthenticationToken.class);
    }
}


On the GitHub page, navigate to the jetty-in-memory-basic-custom-authentication project to see the full source code of this class.

Multiple AuthenticationProvider

Spring Security allows you to declare multiple AuthenticationProvider implementations in your application. They are executed according to the order in which they are declared in the configuration.

The jetty-in-memory-basic-custom-authentication project is modified further, and you have used the newly created CustomAuthenticationProvider as an AuthenticationProvider (Order 1) and the existing as your second  AuthenticationProvider (Order 2) —  MemoryAuthentication:

@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomAuthenticationProvider.class)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    CustomAuthenticationProvidercustomAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic()
            .and()
            .authorizeRequests()
            .antMatchers("/**")
            .authenticated(); // Use Basic authentication
    }

    @Override
    protected void configure(AuthenticationManagerBuilderauth) throws Exception {
        // Custom authentication provider - Order 1
        auth.authenticationProvider(customAuthenticationProvider);
        // Built-in authentication provider - Order 2
        auth.inMemoryAuthentication()
            .withUser("admin")
            .password("{noop}admin@password")
            //{noop} makes sure that the password encoder doesn't do anything
            .roles("ADMIN") // Role of the user
            .and()
            .withUser("user")
            .password("{noop}user@password")
            .credentialsExpired(true)
            .accountExpired(true)
            .accountLocked(true)
            .roles("USER");
    }
}


每当 authenticate 方法执行时没有错误,控制器返回,之后,配置的 AuthenticationProvider 不会被执行。

推荐阅读: 剑指offer 32-2.从上到下打印二叉树

本文链接: Spring安全认证