原文地址
Spring Security实现了 Authentication(认证)和 Access Control (访问控制)
Spring Security在架构上将认证与授权分离,并提供了扩展点
核心对象
SecurityContextHolder, SecurityContext 和 Authentication
SecurityContextHolder 是 SecurityContext 的存放容器,默认使用ThreadLocal存储,意味着SecurityContext在相同线程中的方法都可用
SecurityContext主要是存储应用的 principal 信息,在Spring Security中用 Authentication 来表示
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();if (principal instanceof UserDetails) {String username = ((UserDetails)principal).getUsername();} else {String username = principal.toString();}
Authentication
在实际应用中,常选用实现类UsernamePasswordAuthenticationToken
一个常见的认证过程通常是:创建一个UsernamePasswordAuthenticationToken,交给 AuthenticationManager认证,认证通过则通过 SecurityContextHolder 存放Authentication 信息
UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());Authentication authentication = this.authenticationManager.authenticate(authenticationToken);SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetails与UserDetailsService
UserDetials 是 Spring Security 里的一个关键接口,用来表示一个 principal
提供了认真所需的必要信息,在实际使用里,可以自己实现UserDetials并增加额外信息
Authentication 中的 principal 通常是用户名,可以通过 UserDetailsService 来用过 principal 获取 UserDetail
public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;}
GrantedAuthority
小结
- SecurityContextHolder,用来访问 SecurityContext
- SecurityContext,用来存储Authentication
- Authentication,代表凭证
- GrantedAuthority,代表权限
- UserDetials,用户信息
-
Authentication 认证
AuthenticationManager
接口,只有一个方法:
Authentication authenticate(Authentication authentication) throws AuthenticationException; 方法主要做三件事
- 验证通过,返回 Authentication(通常带上 authenticated=true)
- 认证失败则抛出 AuthenticationException 异常,运行时异常
- 如果无法确定,则返回 null
- ProviderManager,默认实现。委托一组 AuthenticationProvider 实例来实现认证
ProviderManager
包含一组 AuthenticationProvider,执行 authenticate 时,遍历 Providers,然后调用supports,如果支持,则执行遍历当前provider的 authenticate方法,如果一个provider认证成功,则breakAuthenticationProvider
有多种实现,通常为DaoAuthenticationProvider,默认会自动加载,核心是通过 UserDetails 来实现认证。让开发者提供 UserDetailsService 来获取 UserDetails 以及 PasswordEncoder 来检验密码是否有效
要自定义认证,使用 DaoAuthenticationProvider,只需要为其提供 PasswordEncoder 和 UserDetailsService 就可以了
定制 Authentication Managers
Spring Security 提供了一个 Builder 类 AuthenticationManagerBuilder,借助它可以快速实现自定义认证。可以创建基于内存的认证、LDAP认证、JDBC认证,以及添加UserDetailsService和AuthenticationProvider
@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)public class ApplicationSecurity extends WebSecurityConfigurerAdapter {public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,TokenProvider tokenProvider,CorsFilter corsFilter, SecurityProblemSupport problemSupport) {this.authenticationManagerBuilder = authenticationManagerBuilder;this.userDetailsService = userDetailsService;this.tokenProvider = tokenProvider;this.corsFilter = corsFilter;this.problemSupport = problemSupport;}@PostConstructpublic void init() {try {authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());} catch (Exception e) {throw new BeanInitializationException("Security configuration failed", e);}}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class).exceptionHandling().authenticationEntryPoint(problemSupport).accessDeniedHandler(problemSupport).and().csrf().disable().headers().frameOptions().disable().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/register").permitAll().antMatchers("/api/activate").permitAll().antMatchers("/api/authenticate").permitAll().antMatchers("/api/account/reset-password/init").permitAll().antMatchers("/api/account/reset-password/finish").permitAll().antMatchers("/api/profile-info").permitAll().antMatchers("/api/**").authenticated().antMatchers("/management/health").permitAll().antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN).antMatchers("/v2/api-docs/**").permitAll().antMatchers("/swagger-resources/configuration/ui").permitAll().antMatchers("/swagger-ui/index.html").hasAuthority(AuthoritiesConstants.ADMIN).and().apply(securityConfigurerAdapter());}}
授权与访问控制
一旦认证成功,就可以继续进行授权,通过 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
