spring Security源码分析-13种过滤器详解

13种核心过滤器

spring security的13个核心过滤器(按执行顺序陈列):

  1. WebAsyncManagerIntegrationFilter
  2. SecurityContextPersistenceFilter
  3. HeaderWriterFilter
  4. LogoutFilter
  5. UsernamePasswordAuthenticationFilter
  6. DefaultLoginPageGeneratingFilter
  7. DefaultLogoutPageGeneratingFilter
  8. RequestCacheAwareFilter
  9. SecurityContextHolderAwareRequestFilter
  10. AnonymousAuthenticationFilter
  11. SessionManagementFilter
  12. ExceptionTranslationFilter
  13. FilterSecurityInterceptor

security过滤器创建流程

请添加图片描述
在这里插入图片描述

在这里插入图片描述
感兴趣了解详细源码servlet过滤器执行security过滤器详细流程

security过滤器执行流程

请添加图片描述

过滤器介绍

WebAsyncManagerIntegrationFilter

public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);if (securityProcessingInterceptor == null) {asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,new SecurityContextCallableProcessingInterceptor());}filterChain.doFilter(request, response);}}

在这里插入图片描述
主要功能:

  1. 创建WebAsyncManager
  2. 注册SecurityContextCallableProcessingInterceptor,具体使用位置org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler#handleReturnValue
    属于mvc源码部分了,想进一步了解可以参考mvc源码。

SecurityContextPersistenceFilter

public class SecurityContextPersistenceFilter extends GenericFilterBean {static final String FILTER_APPLIED = "__spring_security_scpf_applied";private SecurityContextRepository repo;private boolean forceEagerSessionCreation = false;public SecurityContextPersistenceFilter() {this(new HttpSessionSecurityContextRepository());}public SecurityContextPersistenceFilter(SecurityContextRepository repo) {this.repo = repo;}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);}private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {// ensure that filter is only applied once per requestif (request.getAttribute(FILTER_APPLIED) != null) {chain.doFilter(request, response);return;}request.setAttribute(FILTER_APPLIED, Boolean.TRUE);if (this.forceEagerSessionCreation) {HttpSession session = request.getSession();if (this.logger.isDebugEnabled() && session.isNew()) {this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId()));}}HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);try {SecurityContextHolder.setContext(contextBeforeChainExecution);if (contextBeforeChainExecution.getAuthentication() == null) {logger.debug("Set SecurityContextHolder to empty SecurityContext");}else {if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));}}chain.doFilter(holder.getRequest(), holder.getResponse());}finally {SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();// Crucial removal of SecurityContextHolder contents before anything else.SecurityContextHolder.clearContext();this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());request.removeAttribute(FILTER_APPLIED);this.logger.debug("Cleared SecurityContextHolder to complete request");}}public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {this.forceEagerSessionCreation = forceEagerSessionCreation;}}

主要功能:

  1. 通过SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);加载保存在session中的SecurityContext数据。
    我们知道客户端会将会话ID保存在cookie当中用于后端识别是否是同一个会话,所以当我们通过浏览器清除cookie中sessionID数据的时候,会导致后台找不到对应的session进而加载不到具体的SecurityContext数据,被security其他过滤器视为没有登录,跳转到登录页面。
    在这里插入图片描述

  2. 每次请求结束都会清除SecurityContext数据并将该数据保存到session,方便SecurityContext数据要么来自登录或者来自下次加载session数据,key为“SPRING_SECURITY_CONTEXT”,因此SecurityContext的生命周期是整个请求的生命周期。

			SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();// Crucial removal of SecurityContextHolder contents before anything else.SecurityContextHolder.clearContext();this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());

HeaderWriterFilter

@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HeaderWriterResponse headerWriterResponse = new HeaderWriterResponse(request,response, this.headerWriters);HeaderWriterRequest headerWriterRequest = new HeaderWriterRequest(request,headerWriterResponse);try {filterChain.doFilter(headerWriterRequest, headerWriterResponse);}finally {headerWriterResponse.writeHeaders();}}

主要功能:

  1. response对象重新封装,封装header配置集合
      ① headerWriters包含:
      ② contentTypeOptions
      ③ xssProtection
      ④ cacheControl
      ⑤ hsts
      ⑥ frameOptions
      ⑦ hpkp
      ⑧ contentSecurityPolicy
      ⑨ referrerPolicy
      ⑩ featurePolicy
    具体代码位置org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#getHeaderWriters
	private List<HeaderWriter> getHeaderWriters() {List<HeaderWriter> writers = new ArrayList<>();addIfNotNull(writers, contentTypeOptions.writer);addIfNotNull(writers, xssProtection.writer);addIfNotNull(writers, cacheControl.writer);addIfNotNull(writers, hsts.writer);addIfNotNull(writers, frameOptions.writer);addIfNotNull(writers, hpkp.writer);addIfNotNull(writers, contentSecurityPolicy.writer);addIfNotNull(writers, referrerPolicy.writer);addIfNotNull(writers, featurePolicy.writer);writers.addAll(headerWriters);return writers;}

LogoutFilter

	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);}

主要功能:

  1. 拦截登出页请求/logout
  2. 清空SecurityContext ,handler的实例类是CompositeLogoutHandler
this.handler.logout(request, response, auth);

代码位置:org.springframework.security.web.authentication.logout.CompositeLogoutHandler#logout
在这里插入图片描述
代码位置:org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler#logout

	public void logout(HttpServletRequest request, HttpServletResponse response,Authentication authentication) {Assert.notNull(request, "HttpServletRequest required");if (invalidateHttpSession) {HttpSession session = request.getSession(false);if (session != null) {logger.debug("Invalidating session: " + session.getId());session.invalidate();}}if (clearAuthentication) {SecurityContext context = SecurityContextHolder.getContext();context.setAuthentication(null);}SecurityContextHolder.clearContext();}

3.跳转到登出页

logoutSuccessHandler.onLogoutSuccess(request, response, auth);

logoutSuccessHandler的实力类SimpleUrlLogoutSuccessHandler
代码位置:org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler#onLogoutSuccess

	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,Authentication authentication) throws IOException, ServletException {super.handle(request, response, authentication);}

代码位置:org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler#determineTargetUrl
在这里插入图片描述
重定向到登录页
如果你想自定义logoutSuccessHandler.onLogoutSuccess(request, response, auth),可以通过修改LogoutConfigurer的logoutSuccessHandler,方式:

      http.logout().logoutSuccessHandler((httpServletRequest, httpServletResponse, authentication) -> {// 成功退出登录后返回200状态码// httpServletResponse.setStatus(HttpServletResponse.SC_OK);// 成功退出登录后的需要执行的代码写在这System.out.println("123");});

原因:
org.springframework.security.config.annotation.web.configurers.LogoutConfigurer#createLogoutFilter

	private LogoutFilter createLogoutFilter(H http) throws Exception {logoutHandlers.add(contextLogoutHandler);LogoutHandler[] handlers = logoutHandlers.toArray(new LogoutHandler[logoutHandlers.size()]);LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);result.setLogoutRequestMatcher(getLogoutRequestMatcher(http));result = postProcess(result);return result;}

getLogoutSuccessHandler方法()

	private LogoutSuccessHandler getLogoutSuccessHandler() {LogoutSuccessHandler handler = this.logoutSuccessHandler;if (handler == null) {handler = createDefaultSuccessHandler();}return handler;}

具体为什么修改LogoutConfigurer,请参考文章spring Security源码讲解-WebSecurityConfigurerAdapter

UsernamePasswordAuthenticationFilter

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;}if (logger.isDebugEnabled()) {logger.debug("Request is to process authentication");}Authentication authResult;try {authResult = attemptAuthentication(request, response);if (authResult == null) {// return immediately as subclass has indicated that it hasn't completed// authenticationreturn;}sessionStrategy.onAuthentication(authResult, request, response);}catch (InternalAuthenticationServiceException failed) {logger.error("An internal error occurred while trying to authenticate the user.",failed);unsuccessfulAuthentication(request, response, failed);return;}catch (AuthenticationException failed) {// Authentication failedunsuccessfulAuthentication(request, response, failed);return;}// Authentication successif (continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}successfulAuthentication(request, response, chain, authResult);}

主要功能:

  1. 校验是否是登录路径并且是post请求,如果不是就执行下一个过滤器
		if (!requiresAuthentication(request, response)) {chain.doFilter(request, response);return;}
  1. 进行登录校验(是否有效、登录次数、账号密码)等,因此我们可以在这个过滤器逻辑里面做一些定制,比如根据ip登录限制次数什么的。
authResult = attemptAuthentication(request, response);
  1. 登录
    ①如果登录失败,重定向到/login?error,
unsuccessfulAuthentication(request, response, failed);

在这里插入图片描述failureHandler的对象类SimpleUrlAuthenticationFailureHandler
代码位置:org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#onAuthenticationFailure
在这里插入图片描述

②如果登录成功则改变登录前的sessionID,防止session会话固定攻攻击,默认是有状态session,不改变session对象,只改变sessionID,防止登录前和登陆后sessionID一致,导致被攻击。

sessionStrategy.onAuthentication(authResult, request, response);
  1. 保存登录authentication到SecurityContext
successfulAuthentication(request, response, chain, authResult);

DefaultLoginPageGeneratingFilter

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;boolean loginError = isErrorPage(request);boolean logoutSuccess = isLogoutSuccess(request);if (isLoginUrlRequest(request) || loginError || logoutSuccess) {String loginPageHtml = generateLoginPageHtml(request, loginError,logoutSuccess);response.setContentType("text/html;charset=UTF-8");response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);response.getWriter().write(loginPageHtml);return;}chain.doFilter(request, response);}

主要功能:

  1. 处理登录失败或者登出成功,将登录页的html写回客户端
		boolean loginError = isErrorPage(request);boolean logoutSuccess = isLogoutSuccess(request);if (isLoginUrlRequest(request) || loginError || logoutSuccess) {String loginPageHtml = generateLoginPageHtml(request, loginError,logoutSuccess);response.setContentType("text/html;charset=UTF-8");response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);response.getWriter().write(loginPageHtml);return;}

在这里插入图片描述

DefaultLogoutPageGeneratingFilter

	@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {if (this.matcher.matches(request)) {renderLogout(request, response);} else {filterChain.doFilter(request, response);}}

这个过滤器优点特殊,默认不会执行 renderLogout(request, response);
因为登出会被LogoutFilter过滤器拦截并且重定向登录页/login?logout,因此this.matcher.matches(request)永远是false
如果不想要LogoutFilter登出逻辑,解决办法将LogoutConfigurer从HttpSecurity注销,方式:

        http// 关闭默认注销接口.logout().disable();

主要功能:
1.跳转登出页,通过页面进行注销

renderLogout(request, response);

在这里插入图片描述

RequestCacheAwareFilter

	public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest((HttpServletRequest) request, (HttpServletResponse) response);chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest,response);}

主要功能:
1.获取上次请求失败的目标路径,继续访问。
比如:匿名直接请求需要权限资源失败,ExceptionTranslationFilter过滤器会记录本次的目标请求,登录验证通过后会获取上次的目标请求继续请求。

		HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest((HttpServletRequest) request, (HttpServletResponse) response);

代码位置: org.springframework.security.web.savedrequest.RequestCacheAwareFilter#doFilter

SecurityContextHolderAwareRequestFilter

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {chain.doFilter(this.requestFactory.create((HttpServletRequest) req,(HttpServletResponse) res), res);}

this.requestFactory.create((HttpServletRequest),create方法属于HttpServlet3RequestFactory类.
代码位置:org.springframework.security.web.servletapi.HttpServlet3RequestFactory#create

	public HttpServletRequest create(HttpServletRequest request,HttpServletResponse response) {return new Servlet3SecurityContextHolderAwareRequestWrapper(request,this.rolePrefix, response);}

主要功能:
1.这个过滤器看起来很简单。目的仅仅是实现java ee中servlet api一些接口方法。
一些应用中直接使用getRemoteUser方法、isUserInRole方法,在使用spring security时其实就是通过这个过滤器来实现的。

AnonymousAuthenticationFilter

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {if (SecurityContextHolder.getContext().getAuthentication() == null) {SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));if (logger.isDebugEnabled()) {logger.debug("Populated SecurityContextHolder with anonymous token: '"+ SecurityContextHolder.getContext().getAuthentication() + "'");}}else {if (logger.isDebugEnabled()) {logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"+ SecurityContextHolder.getContext().getAuthentication() + "'");}}chain.doFilter(req, res);}

主要功能:
1.当用户没有登录的时候创建一个用户名为anonymousUser,角色为ROLE_ANONYMOUS的匿名用户

			SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));

createAuthentication方法

	protected Authentication createAuthentication(HttpServletRequest request) {AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,principal, authorities);auth.setDetails(authenticationDetailsSource.buildDetails(request));return auth;}

在这里插入图片描述

SessionManagementFilter

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;if (request.getAttribute(FILTER_APPLIED) != null) {chain.doFilter(request, response);return;}request.setAttribute(FILTER_APPLIED, Boolean.TRUE);if (!securityContextRepository.containsContext(request)) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && !trustResolver.isAnonymous(authentication)) {// The user has been authenticated during the current request, so call the// session strategytry {sessionAuthenticationStrategy.onAuthentication(authentication,request, response);}catch (SessionAuthenticationException e) {// The session strategy can reject the authenticationlogger.debug("SessionAuthenticationStrategy rejected the authentication object",e);SecurityContextHolder.clearContext();failureHandler.onAuthenticationFailure(request, response, e);return;}// Eagerly save the security context to make it available for any possible// re-entrant// requests which may occur before the current request completes.// SEC-1396.securityContextRepository.saveContext(SecurityContextHolder.getContext(),request, response);}else {// No security context or authentication present. Check for a session// timeoutif (request.getRequestedSessionId() != null&& !request.isRequestedSessionIdValid()) {if (logger.isDebugEnabled()) {logger.debug("Requested session ID "+ request.getRequestedSessionId() + " is invalid.");}if (invalidSessionStrategy != null) {invalidSessionStrategy.onInvalidSessionDetected(request, response);return;}}}}chain.doFilter(request, response);}

主要功能:
1.校验用户是否没有被持久化过(保存到session),如果没有就持久化一次

				securityContextRepository.saveContext(SecurityContextHolder.getContext(),request, response);

2.校验session是否过期

				if (request.getRequestedSessionId() != null&& !request.isRequestedSessionIdValid()) {

ExceptionTranslationFilter

	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);logger.debug("Chain processed normally");}catch (IOException ex) {throw ex;}catch (Exception ex) {// Try to extract a SpringSecurityException from the stacktraceThrowable[] causeChain = throwableAnalyzer.determineCauseChain(ex);RuntimeException ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);if (ase == null) {ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);}if (ase != null) {if (response.isCommitted()) {throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", ex);}handleSpringSecurityException(request, response, chain, ase);}else {// Rethrow ServletExceptions and RuntimeExceptions as-isif (ex instanceof ServletException) {throw (ServletException) ex;}else if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}// Wrap other Exceptions. This shouldn't actually happen// as we've already covered all the possibilities for doFilterthrow new RuntimeException(ex);}}}

主要功能:
1.处理FilterSecurityInterceptor过滤器权限校验抛出的异常,如果权限不通过则接受FilterSecurityInterceptor抛出的异常并处理
2.记录访问异常时本次请求

handleSpringSecurityException

代码位置:org.springframework.security.web.access.ExceptionTranslationFilter#handleSpringSecurityException

	private void handleSpringSecurityException(HttpServletRequest request,HttpServletResponse response, FilterChain chain, RuntimeException exception)throws IOException, ServletException {if (exception instanceof AuthenticationException) {logger.debug("Authentication exception occurred; redirecting to authentication entry point",exception);sendStartAuthentication(request, response, chain,(AuthenticationException) exception);}else if (exception instanceof AccessDeniedException) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {logger.debug("Access is denied (user is " + (authenticationTrustResolver.isAnonymous(authentication) ? "anonymous" : "not fully authenticated") + "); redirecting to authentication entry point",exception);sendStartAuthentication(request,response,chain,new InsufficientAuthenticationException(messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication","Full authentication is required to access this resource")));}else {logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",exception);accessDeniedHandler.handle(request, response,(AccessDeniedException) exception);}}}

sendStartAuthentication方法

	protected void sendStartAuthentication(HttpServletRequest request,HttpServletResponse response, FilterChain chain,AuthenticationException reason) throws ServletException, IOException {// SEC-112: Clear the SecurityContextHolder's Authentication, as the// existing Authentication is no longer considered validSecurityContextHolder.getContext().setAuthentication(null);requestCache.saveRequest(request, response);logger.debug("Calling Authentication entry point.");authenticationEntryPoint.commence(request, response, reason);}

requestCache.saveRequest(request, response);记录本次请求

FilterSecurityInterceptor

	public void invoke(FilterInvocation fi) throws IOException, ServletException {if ((fi.getRequest() != null)&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)&& observeOncePerRequest) {// filter already applied to this request and user wants us to observe// once-per-request handling, so don't re-do security checkingfi.getChain().doFilter(fi.getRequest(), fi.getResponse());}else {// first time this request being called, so perform security checkingif (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);}}

核心代码

InterceptorStatusToken token = super.beforeInvocation(fi);

代码位置:org.springframework.security.access.intercept.AbstractSecurityInterceptor#beforeInvocation

	protected InterceptorStatusToken beforeInvocation(Object object) {Assert.notNull(object, "Object was null");final boolean debug = logger.isDebugEnabled();if (!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: "+ getSecureObjectClass());}Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);if (attributes == null || attributes.isEmpty()) {if (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'");}if (debug) {logger.debug("Public object - authentication not attempted");}publishEvent(new PublicInvocationEvent(object));return null; // no further work post-invocation}if (debug) {logger.debug("Secure object: " + object + "; Attributes: " + attributes);}if (SecurityContextHolder.getContext().getAuthentication() == null) {credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound","An Authentication object was not found in the SecurityContext"),object, attributes);}Authentication authenticated = authenticateIfRequired();// Attempt authorizationtry {this.accessDecisionManager.decide(authenticated, object, attributes);}catch (AccessDeniedException accessDeniedException) {publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,accessDeniedException));throw accessDeniedException;}if (debug) {logger.debug("Authorization successful");}if (publishAuthorizationSuccess) {publishEvent(new AuthorizedEvent(object, attributes, authenticated));}// Attempt to run as a different userAuthentication runAs = this.runAsManager.buildRunAs(authenticated, object,attributes);if (runAs == null) {if (debug) {logger.debug("RunAsManager did not change Authentication object");}// no further work post-invocationreturn new InterceptorStatusToken(SecurityContextHolder.getContext(), false,attributes, object);}else {if (debug) {logger.debug("Switching to RunAs Authentication: " + runAs);}SecurityContext origCtx = SecurityContextHolder.getContext();SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());SecurityContextHolder.getContext().setAuthentication(runAs);// need to revert to token.Authenticated post-invocationreturn new InterceptorStatusToken(origCtx, true, attributes, object);}}

主要功能:
1.获取路径和角色的映射关系

		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

如我们在这里配置的映射数据
在这里插入图片描述
2.是否每次都要重新登录一次用户(开启的话,可以防止用户此时过期,但是性能降低,默认不开启,只在UserNamepasswordFilter过滤器中进行登录)

Authentication authenticated = authenticateIfRequired();

3.校验当前用户访问的路径是否满足该路径配置权限

this.accessDecisionManager.decide(authenticated, object, attributes);

过程:
1.获取当前登录用户的角色
在这里插入图片描述
2.根据当前登录object,从attributes中拿到需要当前用户拥有的角色集合
3.判断当前登录用户信息authenticated中的authorities属性是否存在访问路径必要必要集合元素,如果存在就通过,否则无权限抛AccessDeniedException异常被FilterSecurityInterceptor过滤器捕获。
因此我们可以在FilterSecurityInterceptor过程中实现自定义权限校验逻辑

需要两不操作:

	Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);

需要改变FilterSecurityInterceptor过滤器的俩个实例对象securityMetadataSource和accessDecisionManager
实现代码:

@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
@Configuration
public class MyConfig{@Autowiredprivate FilterChainProxy filterChainProxy;@PostConstructpublic void postConstruct() {List<SecurityFilterChain> securityFilterChainList = filterChainProxy.getFilterChains();List<Filter> filters = securityFilterChainList.get(0).getFilters();for(Filter filter : filters){if(filter instanceof FilterSecurityInterceptor){FilterSecurityInterceptor filterSecurityInterceptor = (FilterSecurityInterceptor) filter;filterSecurityInterceptor.setSecurityMetadataSource(new MyFilterInvocationSecurityMetadataSource());filterSecurityInterceptor.setAccessDecisionManager(new MyAccessDecisionManager());}}}public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource{@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {System.out.println("加载路径所需角色");return null;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> clazz) {return false;}}public class MyAccessDecisionManager implements AccessDecisionManager{@Overridepublic void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {System.out.println("进行权限校验");}@Overridepublic boolean supports(ConfigAttribute attribute) {return false;}@Overridepublic boolean supports(Class<?> clazz) {return false;}}}

这样我们就可以动态加载和校验权限,不用在WebSecurityConfigurerAdapter里配置了
在这里插入图片描述

无论你的梦想有多么高远,记住,一切皆有可能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/628463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java获取已经发送谷歌邮件的打开状态

1.前言 现在网上的方案都是在邮件里面插入一张图片的地址&#xff0c;当收件人打开之后&#xff0c;就会发送请求到指定路径的服务器上&#xff0c;然后在请求的controller里面处理邮件的状态&#xff0c;这个方案确实是行得通的&#xff0c;本文章只是给大家避个坑&#xff0…

HNU-模式识别-作业1-视频监控系统

模式识别-作业1 计科210X 甘晴void 202108010XXX 【评分&#xff1a;98/100】 题目&#xff1a; 查阅相关技术资料&#xff0c;根据自己家庭相应的情况&#xff0c;设计一个视频监控系统。要求&#xff1a; 系统功能说明系统布线图及说明系统软硬件配置说明 饱和式自家用…

C++ mapset

目录 相关知识介绍&#xff1a; 一、set 1、set的介绍 2、set的使用 1. set的模板参数列表 2. set的构造 3. set的迭代器 4. set的容量 5. set修改操作 6. 举例演示 二、multiset 1、multiset的介绍 2、multiset的使用 三、map 1、map的介绍 2、map的使用 1.…

基于Java SSM框架实现摄影器材租赁系统项目【项目源码+论文说明】

基于java的SSM框架实现摄影器材租赁系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

跨境电商账号频繁?你的IP可能“不干净”了

疫情促进了跨境电商行业的加速发展&#xff0c;许多卖家也抓住了这波流量红利&#xff0c;跨境电商月入数万&#xff0c;数十万甚至数百万的造福神话也不断在上演&#xff0c;但由于国内外电商运营模式不同&#xff0c;多店运营、用户数据收集、刷单等行为都受到了国外平台的严…

vue前端开发自学,异步加载组件,提升用户端的客户体验度

vue前端开发自学,异步加载组件,提升用户端的客户体验度&#xff01;现实项目开发时&#xff0c;组件的数量非常庞大&#xff0c;如果都是一口气加载完&#xff0c;对手机用户来说&#xff0c;体验度会很差。因此&#xff0c;非常有必要使用异步加载。 那就是&#xff0c;用到了…

【WEB API自动化测试】接口文档与在线测试

这一篇我们主要介绍如何做API帮助文档&#xff0c;给API的调用人员介绍各个 API的功能, 输入参数&#xff0c;输出参数, 以及在线测试 API功能(这个也是方便我们自己开发调试) 我们先来看看我们的API最终帮助文档及在线测试最终达到的效果: 概要图 GET API 添加产品API: 删除…

mysql高级使用教程

mysql体系结构 1.连接层&#xff1a;主要就是做客户端的连接。 2.服务层&#xff1a;主要就是 缓存&#xff0c;分析器&#xff0c;优化器&#xff0c;执行器。 3.引擎层&#xff1a;就是一些存储引擎&#xff0c;包括索引的存储结构。 4.存储层&#xff1a;数据存储的磁盘…

K8S--Ingress的作用

原文网址&#xff1a;K8S--Ingress的作用-CSDN博客 简介 本文介绍K8S的Ingress的作用。 ----------------------------------------------------------------------------------------------- 分享Java真实高频面试题&#xff0c;吊打面试官&#xff1a; Java后端真实面试题…

Java SE入门及基础(8)

关系运算符和逻辑运算符 1. 关系运算符 关系运算符包含 > < > < ! boolean result 2 > 3 ; boolean result1 10 10 ; 关系运算符比较的结果是一个布尔值 2. 逻辑运算符 逻辑运算符包含&#xff1a; 逻辑与 &&&#xff…

uniapp自带的选择日期的使用

年月日的下拉框 直接看代码 <view class"uni-title uni-common-pl">日期选择器</view><view class"uni-list"><view class"uni-list-cell"><view class"uni-list-cell-left">当前选择</view>&…

如何隐藏服务器真实IP地址,隐藏服务器IP有什么好处

首先我们介绍了隐藏服务器IP的概念及工作模式&#xff0c;接着阐述了其对于DDoS攻击的防护作用。然后介绍了如何利用隐藏服务器IP增加系统性能和稳定性。接着我们讲述了如何隐藏服务器IP防止黑客攻击&#xff0c;最后总结了隐藏服务器IP在保护服务器和用户数据方面发挥的作用。…

关于html导出word总结一

总结 测试结果不理想&#xff0c;html-to-docx 和 html-docx-js 最终导出的结果 都 差强人意&#xff0c;效果可以见末尾的附图 环境 "electron": "24.3.0" 依赖库 html-docx-js html-docx-js - npm html-to-docx html-to-docx - npm file-saver…

消息中间件作用

一&#xff1a;消息队列的主要作用是什么&#xff1f; 1.消息队列的特性&#xff1a; 业务无关&#xff0c;一个具有普适性质的消息队列组件不需要考虑上层的业务模型&#xff0c;只做好消息的分发就可以了&#xff0c;上层业务的不同模块反而需要依赖消息队列所定义的规范进行…

Docker部署Jira、Confluence、Bitbucket、Bamboo、Crowd,Atlassian全家桶

文章目录 省流&#xff1a;注意&#xff1a;解决方案&#xff1a; 1.docker-compose文件2.其他服务都正常启动&#xff0c;唯独Bitbucket不行。日志错误刚启动时候重启后查询分析原因再针对第一点排查看样子是安装的bitbucket和系统环境有冲突问题&#xff1f; 结论&#xff1a…

React Native 原生组件回调JS层方法和 JS 层调用原生组件的事件方法

一、原生组件回调 JS 层提供的事件方法 比如 TextInput 组件 onChangeText 属性&#xff0c;输入事件是发生在原生层的但是需要通知 JS 层发生了变化&#xff0c;并执行 JS 层的方法。 1、给原生组件添加一个按钮用于触发原生事件方法 在 XML 中添加一个按钮 为了方便让 Inf…

科荣AIO UtilServlet 前台RCE漏洞复现

0x01 产品简介 科荣AIO 企业⼀体化管理解决⽅案 通过ERPERP(进销存财务)、OAOA(办公⾃动化)、CRMCRM(客⼾关系管理)、UDPUDP(⾃定义平台),集电⼦商务平台、⽀付平台、ERP 平台、微信平台、移动APP 等解决了众多企业客⼾在管理过程中跨部⻔、多功能、需求多变等通⽤及…

微信小程序(二)事件绑定

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 点击事件绑定注册页面设置页面初始化数据事件处理函数的实现更新数据并更新视图 源码&#xff1a; index.wxml <!-- 页面的数据绑定 --> <view>{{msg}}</view> <!-- 绑定点击事件 --> …

【镜像制作】OS云主机镜像的制作——以H3C为例

一、云主机镜像简介 1&#xff0e;云主机镜像 云主机镜像不同于容器镜像&#xff0c;是一个含有引导分区、操作系统以及必要应用的单一文件&#xff0c;可以理解成是对整个系统安装光盘所有数据的克隆文件。云用户在创建和申请云主机时可通过选择不同的镜像来快速获取相应操作…

linux创建文件

创建文件夹&#xff1a; mkdir folder_name其中&#xff0c;folder_name是想要创建的文件夹的名称。 例如&#xff0c;如果想在当前目录下创建一个名为 "my_folder" 的文件夹&#xff0c;可以运行以下命令&#xff1a; mkdir my_folder如果想在特定路径下创建文件…