Spring Security 源码阅读笔记
一、核心架构概述
Spring Security的核心是基于过滤器链(Filter Chain)的认证和授权机制。通过分析源码,我们可以看到它的主要组件和执行流程。
1.1 核心组件
- SecurityContextHolder: 安全上下文的存储策略
- Authentication: 认证信息的抽象
- AuthenticationManager: 认证管理器
- SecurityFilterChain: 安全过滤器链
- UserDetailsService: 用户信息获取服务
- PasswordEncoder: 密码编码器
- AccessDecisionManager: 访问决策管理器
二、认证流程源码分析
2.1 SecurityContextHolder
public class SecurityContextHolder {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
public static SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
// 其它方法...
}
SecurityContextHolder负责存储当前用户的安全上下文,默认使用ThreadLocal存储,确保每个线程都有独立的安全上下文。
2.2 UsernamePasswordAuthenticationFilter
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);
return this.getAuthenticationManager().authenticate(authRequest);
}
// 其它方法...
}
这个过滤器负责处理表单登录的认证请求,从请求中获取用户名和密码,然后创建认证令牌交给AuthenticationManager处理。
2.3 ProviderManager
public class ProviderManager implements AuthenticationManager {
private List<AuthenticationProvider> providers;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
try {
Authentication result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
return result;
}
}
catch (AuthenticationException e) {
lastException = e;
}
}
if (lastException != null) {
throw lastException;
}
throw new ProviderNotFoundException("No AuthenticationProvider found for " + toTest.getName());
}
// 其它方法...
}
ProviderManager是AuthenticationManager的实现,它委托一系列AuthenticationProvider来处理认证请求。
2.4 DaoAuthenticationProvider
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private UserDetailsService userDetailsService;
private PasswordEncoder passwordEncoder;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
throw new BadCredentialsException("Bad credentials");
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
throw new BadCredentialsException("Bad credentials");
}
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
try {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException notFound) {
throw notFound;
}
catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
}
// 其它方法...
}
DaoAuthenticationProvider通过UserDetailsService获取用户信息,然后使用PasswordEncoder验证密码。
三、授权流程源码分析
3.1 FilterSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
invoke(new FilterInvocation(request, response, chain));
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
else {
if (fi.getRequest() != null && observeOncePerRequest) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
// 其它方法...
}
FilterSecurityInterceptor是过滤器链中最后一个过滤器,负责对请求进行访问控制决策。
3.2 AccessDecisionManager
public interface AccessDecisionManager {
void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
}
AccessDecisionManager接口定义了授权决策的方法,由具体实现类决定是否允许访问。
3.3 AffirmativeBased
public class AffirmativeBased extends AbstractAccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// 如果没有voter投赞成票,根据allowIfAllAbstainDecisions决定是否允许访问
checkAllowIfAllAbstainDecisions();
}
// 其它方法...
}
AffirmativeBased是AccessDecisionManager的一个实现,只要有一个投票器投票通过,就允许访问。
四、过滤器链构建过程
4.1 WebSecurity
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware {
private List<SecurityFilterChain> securityFilterChains = new ArrayList<>();
@Override
protected Filter performBuild() throws Exception {
// ...
int chainSize = this.securityFilterChains.size();
if (chainSize > 0) {
return VirtualFilterChain.createChainProxy(this.securityFilterChains);
}
// ...
}
// 其它方法...
}
WebSecurity负责构建过滤器链。
4.2 HttpSecurity
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
@Override
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
// 其它方法...
}
HttpSecurity负责配置单个SecurityFilterChain中的过滤器。
五、常见过滤器解析
在Spring Security中,请求会经过一系列过滤器,下面是一些关键过滤器的源码分析:
5.1 SecurityContextPersistenceFilter
public class SecurityContextPersistenceFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
}
}
// 其它方法...
}
SecurityContextPersistenceFilter负责在请求处理前加载SecurityContext,在请求处理后保存SecurityContext。
5.2 LogoutFilter
public class LogoutFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (requiresLogout(request, response)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (logger.isDebugEnabled()) {
logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
}
this.handler.logout(request, response, auth);
logoutSuccessHandler.onLogoutSuccess(request, response, auth);
return;
}
chain.doFilter(request, response);
}
// 其它方法...
}
LogoutFilter处理用户退出登录的请求。
六、注解式安全配置实现原理
6.1 @EnableWebSecurity
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class, HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
@EnableWebSecurity导入了WebSecurityConfiguration等配置类。
6.2 WebSecurityConfiguration
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration {
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
webSecurity.apply(adapter);
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
return webSecurity.build();
}
// 其它方法...
}
WebSecurityConfiguration负责创建springSecurityFilterChain。
6.3 @PreAuthorize源码
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Repeatable(PreAuthorizes.class)
public @interface PreAuthorize {
String value();
}
@PreAuthorize是方法级的安全注解,用于指定访问控制表达式。
6.4 MethodSecurityInterceptor
public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
InterceptorStatusToken token = super.beforeInvocation(mi);
Object result;
try {
result = mi.proceed();
}
finally {
super.finallyInvocation(token);
}
return super.afterInvocation(token, result);
}
// 其它方法...
}
MethodSecurityInterceptor拦截带有安全注解的方法调用,进行访问控制决策。
七、OAuth2集成源码分析
7.1 OAuth2LoginConfigurer
public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractAuthenticationFilterConfigurer<B, OAuth2LoginConfigurer<B>, OAuth2LoginAuthenticationFilter> {
@Override
public void init(B http) throws Exception {
OAuth2LoginAuthenticationFilter authenticationFilter = new OAuth2LoginAuthenticationFilter(
this.authorizationRequestRepository, this.authorizationRequestRepository);
authenticationFilter.setAuthenticationManager(
this.authenticationManager(http));
// 设置各种属性...
this.setAuthenticationFilter(authenticationFilter);
super.init(http);
}
// 其它方法...
}
OAuth2LoginConfigurer配置OAuth2登录流程。
7.2 OAuth2AuthorizationRequestRedirectFilter
public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String registrationId = this.resolveRegistrationId(request);
if (registrationId == null) {
filterChain.doFilter(request, response);
return;
}
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
if (clientRegistration == null) {
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
}
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver
.resolve(request, clientRegistration);
if (authorizationRequest == null) {
throw new IllegalStateException(
"Unable to resolve OAuth2 Authorization Request for Client Registration: " + registrationId);
}
this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
URI redirectUri = URI.create(authorizationRequest.getAuthorizationRequestUri());
this.authorizationRedirectStrategy.sendRedirect(request, response, redirectUri.toString());
}
// 其它方法...
}
OAuth2AuthorizationRequestRedirectFilter负责处理OAuth2授权请求的重定向。
八、总结与心得
通过对Spring Security源码的阅读和分析,我对其核心工作流程有了更深入的理解:
- 安全过滤器链是Spring Security的核心,它通过一系列过滤器来处理HTTP请求的安全性。
- 认证流程主要通过AuthenticationManager和AuthenticationProvider来实现,支持多种认证方式。
- 授权决策由AccessDecisionManager负责,通过投票机制来决定是否允许访问。
- Spring Security的配置非常灵活,可以通过Java配置、XML配置或注解来实现。
- Spring Security与OAuth2的集成是通过专门的过滤器和配置类来实现的。
评论