一 web.xml配置文件
- 配置2个filter
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
springSecurityFilterChain
/*
- spring security是在DelegatingFilterProxy中进行处理
- tomcat初始化DelegatingFilterProxy类对象
- 从spring管理的bean中获取
FilterChainProxy
,赋值为Filter delegate
-
FilterChainProxy
类型bean通过xml标签定义,解析后交给spring容器管理。
二 spring xml配置文件
- org.springframework.security.config.SecurityNamespaceHandler 注册xml标签解析函数
- HttpSecurityBeanDefinitionParser http标签解析函数
- 一个http标签解析成一个DefaultSecurityFilterChain类型过滤串,按匹配规则
RequestMatcher requestMatcher
对目标url执行一组List
过滤操作。filters - 所有的DefaultSecurityFilterChain用于初始化
filterChainProxy
属性List
。filterChainProxy对象交给spring容器管理filterChains - 第二个http标签初始化了一组filter,当有请求时依次调用各filter的doFilter()函数处理请求。
三 filter
3.1 SecurityContextPersistenceFilter
- 实例化
public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
this.forceEagerSessionCreation = false;
this.repo = repo;
}
- http标签配置
security-context-repository-ref="myCookieSecurityContextRepository"
为构造函数参数repo - 过滤处理函数
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
if(request.getAttribute("__spring_security_scpf_applied") != null) {//只处理一次
chain.doFilter(request, response);
} else {
boolean debug = this.logger.isDebugEnabled();
request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
if(this.forceEagerSessionCreation) {
HttpSession session = request.getSession();
if(debug && session.isNew()) {
this.logger.debug("Eagerly created session: " + session.getId());
}
}
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
//从请求中获取用户认证信息,cookie或session中保存的认证信息
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
boolean var13 = false;
try {
var13 = true;
//默认threadlocal方式保存变量,保存filter处理前的认证信息 SecurityContextHolder.setContext(contextBeforeChainExecution);
//执行后续filter处理
chain.doFilter(holder.getRequest(), holder.getResponse());
var13 = false;
} finally {
if(var13) {//异常发生
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute("__spring_security_scpf_applied");
if(debug) {
this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
//清理缓存数据
SecurityContextHolder.clearContext();
//保存认证信息
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
//删除处理标记 request.removeAttribute("__spring_security_scpf_applied");
if(debug) {
this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}
3.2 WebAsyncManagerIntegrationFilter
- 支持异步处理
- 只调用一次处理
- 过滤处理
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);//初始化并注册到request中
SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor)asyncManager.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
if(securityProcessingInterceptor == null) {//初始化注册到asyncManager中
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, new SecurityContextCallableProcessingInterceptor());
}
filterChain.doFilter(request, response);
}
3.3 LogoutFilter
- 实例化
public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
Assert.notEmpty(handlers, "LogoutHandlers are required");
//退出处理函数,清理session及cookie及缓存中的认证信息
this.handlers = Arrays.asList(handlers);
Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) || UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
if(StringUtils.hasText(logoutSuccessUrl)) {
//指定退出后的跳转url urlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
}
this.logoutSuccessHandler = urlLogoutSuccessHandler;
this.setFilterProcessesUrl("/j_spring_security_logout");
}
- 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
if(!this.requiresLogout(request, response)) {//匹配函数未匹配
chain.doFilter(request, response);
} else {
//获取认证信息
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
}
Iterator i$ = this.handlers.iterator();
while(i$.hasNext()) {//遍历退出处理函数,进行处理
LogoutHandler handler = (LogoutHandler)i$.next();
handler.logout(request, response, auth);
}
//使用sendRedirect(),跳转到退出url
this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
}
}
- sendRedirect()与forward()
sendRedirect就是服务端根据逻辑,发送一个状态码(Location ,状态码320),告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。
3.4 SecurityContextHolderAwareRequestFilter
- 获取servlet版本信息
private boolean isServlet3() {
return ClassUtils.hasMethod(ServletRequest.class, "startAsync", new Class[0]);
}
- 根据servlet版本信息初始化请求工厂
this.requestFactory = (HttpServletRequestFactory)(this.isServlet3()?this.createServlet3Factory(this.rolePrefix):new HttpServlet25RequestFactory(this.trustResolver, this.rolePrefix));
- servlet3工厂
private HttpServletRequestFactory createServlet3Factory(String rolePrefix) {
HttpServlet3RequestFactory factory = new HttpServlet3RequestFactory(rolePrefix);
factory.setTrustResolver(this.trustResolver);
factory.setAuthenticationEntryPoint(this.authenticationEntryPoint);
factory.setAuthenticationManager(this.authenticationManager);
factory.setLogoutHandlers(this.logoutHandlers);
return factory;
}
- 注入认证管理器AuthenticationManager
- 注入认证入口函数AuthenticationEntryPoint
- 注入logout处理函数
- 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//请求工厂创建一个请求 chain.doFilter(this.requestFactory.create((HttpServletRequest)req, (HttpServletResponse)res), res);
}
-
请求的类关系图
3.5 AnonymousAuthenticationFilter
- 支持匿名用户认证,用户名 anonymousUser,角色ROLE_ANONYMOUS
- 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if(this.applyAnonymousForThisRequest((HttpServletRequest)req)) {
if(SecurityContextHolder.getContext().getAuthentication() == null) {//无认证信息
SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));//创建匿名用户认证
if(this.logger.isDebugEnabled()) {
this.logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
}
} else if(this.logger.isDebugEnabled()) {
this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
//使用匿名用户进行后续处理
chain.doFilter(req, res);
}
3.6 ExceptionTranslationFilter
- 异常处理
- 实例化
public ExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint, RequestCache requestCache) {
this.accessDeniedHandler = new AccessDeniedHandlerImpl();//禁止访问处理
this.authenticationTrustResolver = new AuthenticationTrustResolverImpl();
this.throwableAnalyzer = new ExceptionTranslationFilter.DefaultThrowableAnalyzer();//异常解析
this.requestCache = new HttpSessionRequestCache();
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
Assert.notNull(requestCache, "requestCache cannot be null");
//认证入口,异常发生时,调用认证入口函数
this.authenticationEntryPoint = authenticationEntryPoint;
this.requestCache = requestCache;
}
- 过滤处理
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
try {
chain.doFilter(request, response);
this.logger.debug("Chain processed normally");
} catch (IOException var9) {//io异常不处理
throw var9;
} catch (Exception var10) {
//解析异常
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if(ase == null) {
ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if(ase == null) {//解析失败,抛出异常
if(var10 instanceof ServletException) {
throw (ServletException)var10;
}
if(var10 instanceof RuntimeException) {
throw (RuntimeException)var10;
}
throw new RuntimeException(var10);
}
//解析成功,进行处理
this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
}
}
- 异常解析结果处理
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
if(exception instanceof AuthenticationException) {
this.logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception);
//认证异常,重新认证
this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
} else if(exception instanceof AccessDeniedException) {//权限异常
if(this.authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {//匿名用户,重新认证
this.logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point", exception);
this.sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException("Full authentication is required to access this resource"));
} else {//调用认证失败处理函数处理
this.logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception);
this.accessDeniedHandler.handle(request, response, (AccessDeniedException)exception);
}
}
}
3.7 FilterSecurityInterceptor
- doFilter处理
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);//初始化
this.invoke(fi);
}
- invoke()
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if(fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} else {
if(fi.getRequest() != null) {//只处理一次
fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
}
//调用各模版函数
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, (Object)null);
}
}
- beforeInvocation()
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
boolean debug = this.logger.isDebugEnabled();
if(!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + this.getSecureObjectClass());
} else {
//获取url匹配pattern规则的`intercept-url`配置
Collection attributes = this.obtainSecurityMetadataSource().getAttributes(object);
if(attributes != null && !attributes.isEmpty()) {
if(debug) {
this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
}
//未认证会创建匿名认证信息 if(SecurityContextHolder.getContext().getAuthentication() == null) {
this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
}
//检查是否需要进行认证,一种是匿名认证,一种是配置了重复认证。
Authentication authenticated = this.authenticateIfRequired();
try {//授权校验处理,失败则抛AccessDeniedException
this.accessDecisionManager.decide(authenticated, object, attributes);
} catch (AccessDeniedException var7) {
this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));//发送通知事件
throw var7;//异常处理函数中处理
}
if(debug) {
this.logger.debug("Authorization successful");
}
//通知授权结果
if(this.publishAuthorizationSuccess) {
this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
if(runAs == null) {
if(debug) {
this.logger.debug("RunAsManager did not change Authentication object");
}
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
} else {
if(debug) {
this.logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs);
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
} else if(this.rejectPublicInvocations) {
throw new IllegalArgumentException("Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. " + "This indicates a configuration error because the " + "rejectPublicInvocations property is set to 'true'");
} else {
if(debug) {
this.logger.debug("Public object - authentication not attempted");
}
this.publishEvent(new PublicInvocationEvent(object));
return null;
}
}
}
- finallyInvocation
protected void finallyInvocation(InterceptorStatusToken token) {
if(token != null && token.isContextHolderRefreshRequired()) {//配置每次都刷新缓存的认证信息,默认配置为否
if(this.logger.isDebugEnabled()) {
this.logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());
}
SecurityContextHolder.setContext(token.getSecurityContext());
}
}
- afterInvocation
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
if(token == null) {
return returnedObject;
} else {
this.finallyInvocation(token);
if(this.afterInvocationManager != null) {
try {
//处理后校验
returnedObject = this.afterInvocationManager.decide(token.getSecurityContext().getAuthentication(), token.getSecureObject(), token.getAttributes(), returnedObject);
} catch (AccessDeniedException var5) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(), token.getSecurityContext().getAuthentication(), var5);
this.publishEvent(event);
throw var5;
}
}
return returnedObject;
}
}