原文地址
Spring Security实现了 Authentication(认证)和 Access Control (访问控制)
Spring Security在架构上将认证与授权分离,并提供了扩展点

核心对象

主要代码在 spring-security-core

SecurityContextHolder, SecurityContext 和 Authentication

SecurityContextHolder 是 SecurityContext 的存放容器,默认使用ThreadLocal存储,意味着SecurityContext在相同线程中的方法都可用
SecurityContext主要是存储应用的 principal 信息,在Spring Security中用 Authentication 来表示

  1. Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  2. if (principal instanceof UserDetails) {
  3. String username = ((UserDetails)principal).getUsername();
  4. } else {
  5. String username = principal.toString();
  6. }

Authentication

在实际应用中,常选用实现类UsernamePasswordAuthenticationToken
一个常见的认证过程通常是:创建一个UsernamePasswordAuthenticationToken,交给 AuthenticationManager认证,认证通过则通过 SecurityContextHolder 存放Authentication 信息

  1. UsernamePasswordAuthenticationToken authenticationToken =
  2. new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
  3. Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
  4. SecurityContextHolder.getContext().setAuthentication(authentication);

UserDetails与UserDetailsService

UserDetials 是 Spring Security 里的一个关键接口,用来表示一个 principal
提供了认真所需的必要信息,在实际使用里,可以自己实现UserDetials并增加额外信息
Authentication 中的 principal 通常是用户名,可以通过 UserDetailsService 来用过 principal 获取 UserDetail

  1. public interface UserDetailsService {
  2. UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
  3. }

GrantedAuthority

GrantedAuthority可以理解为角色

小结

  • SecurityContextHolder,用来访问 SecurityContext
  • SecurityContext,用来存储Authentication
  • Authentication,代表凭证
  • GrantedAuthority,代表权限
  • UserDetials,用户信息
  • UserDetailsService,获取用户信息

    Authentication 认证

    AuthenticationManager

    接口,只有一个方法:
    Authentication authenticate(Authentication authentication) throws AuthenticationException;

  • 方法主要做三件事

    • 验证通过,返回 Authentication(通常带上 authenticated=true)
    • 认证失败则抛出 AuthenticationException 异常,运行时异常
    • 如果无法确定,则返回 null
  • ProviderManager,默认实现。委托一组 AuthenticationProvider 实例来实现认证

    ProviderManager

    包含一组 AuthenticationProvider,执行 authenticate 时,遍历 Providers,然后调用supports,如果支持,则执行遍历当前provider的 authenticate方法,如果一个provider认证成功,则break

    AuthenticationProvider

    有多种实现,通常为DaoAuthenticationProvider,默认会自动加载,核心是通过 UserDetails 来实现认证。让开发者提供 UserDetailsService 来获取 UserDetails 以及 PasswordEncoder 来检验密码是否有效

要自定义认证,使用 DaoAuthenticationProvider,只需要为其提供 PasswordEncoder 和 UserDetailsService 就可以了

定制 Authentication Managers

Spring Security 提供了一个 Builder 类 AuthenticationManagerBuilder,借助它可以快速实现自定义认证。可以创建基于内存的认证、LDAP认证、JDBC认证,以及添加UserDetailsService和AuthenticationProvider

  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
  4. public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
  5. public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,TokenProvider tokenProvider,CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
  6. this.authenticationManagerBuilder = authenticationManagerBuilder;
  7. this.userDetailsService = userDetailsService;
  8. this.tokenProvider = tokenProvider;
  9. this.corsFilter = corsFilter;
  10. this.problemSupport = problemSupport;
  11. }
  12. @PostConstruct
  13. public void init() {
  14. try {
  15. authenticationManagerBuilder
  16. .userDetailsService(userDetailsService)
  17. .passwordEncoder(passwordEncoder());
  18. } catch (Exception e) {
  19. throw new BeanInitializationException("Security configuration failed", e);
  20. }
  21. }
  22. @Override
  23. protected void configure(HttpSecurity http) throws Exception {
  24. http
  25. .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
  26. .exceptionHandling()
  27. .authenticationEntryPoint(problemSupport)
  28. .accessDeniedHandler(problemSupport)
  29. .and()
  30. .csrf()
  31. .disable()
  32. .headers()
  33. .frameOptions()
  34. .disable()
  35. .and()
  36. .sessionManagement()
  37. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  38. .and()
  39. .authorizeRequests()
  40. .antMatchers("/api/register").permitAll()
  41. .antMatchers("/api/activate").permitAll()
  42. .antMatchers("/api/authenticate").permitAll()
  43. .antMatchers("/api/account/reset-password/init").permitAll()
  44. .antMatchers("/api/account/reset-password/finish").permitAll()
  45. .antMatchers("/api/profile-info").permitAll()
  46. .antMatchers("/api/**").authenticated()
  47. .antMatchers("/management/health").permitAll()
  48. .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
  49. .antMatchers("/v2/api-docs/**").permitAll()
  50. .antMatchers("/swagger-resources/configuration/ui").permitAll()
  51. .antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN)
  52. .and()
  53. .apply(securityConfigurerAdapter());
  54. }
  55. }

授权与访问控制

一旦认证成功,就可以继续进行授权,通过 AccessDecisionManager 来实现。
框架有三种实现,默认是AffirmativeBased,通过AccessDecisionVoter决策
ConfigAttribute其实就是上面ApplicationSecurity的configure里的

web security 如何实现

Web层中的 Spring Security (用于UI和HTTP后端) 基于Servlet Filters
Spring Security 通过 FilterChainProxy 作为单一的Filter注册到Web层,Proxy内部的Filter
FilterChainProxy相当于一个filter的容器,通过VirtualFilterChain来依次调用各个内部filter