前言

Spring Security介绍中,我们分析到了根据请求获取匹配的SecurityFilterChain,这个类中包含了一组Filter

接下来我们从这些Filter开始探究之旅

Spring Security Filter简介

AuthenticationFilter中的attemptAuthentication方法调用AuthenticationManager(interface)的authenticate方法,AuthenticationManager的实际是现实ProvideManager

ProviderManager 有一个配置好的认证提供者列表(AuthenticationProvider), ProviderManager 会把收到的 UsernamePasswordAuthenticationToken 对象传递给列表中的每一个 AuthenticationProvider 进行认证.

认证过程

AuthenticationProvider接口

public interface AuthenticationProvider {
// ~ Methods
// ======================================================================================================== /**
* Performs authentication with the same contract as
* {@link org.springframework.security.authentication.AuthenticationManager#authenticate(Authentication)}
* .
*
* @param authentication the authentication request object.
*
* @return a fully authenticated object including credentials. May return
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
* authentication of the passed <code>Authentication</code> object. In such a case,
* the next <code>AuthenticationProvider</code> that supports the presented
* <code>Authentication</code> class will be tried.
*
* @throws AuthenticationException if authentication fails.
*/
Authentication authenticate(Authentication authentication)
throws AuthenticationException; /**
* Returns <code>true</code> if this <Code>AuthenticationProvider</code> supports the
* indicated <Code>Authentication</code> object.
* <p>
* Returning <code>true</code> does not guarantee an
* <code>AuthenticationProvider</code> will be able to authenticate the presented
* instance of the <code>Authentication</code> class. It simply indicates it can
* support closer evaluation of it. An <code>AuthenticationProvider</code> can still
* return <code>null</code> from the {@link #authenticate(Authentication)} method to
* indicate another <code>AuthenticationProvider</code> should be tried.
* </p>
* <p>
* Selection of an <code>AuthenticationProvider</code> capable of performing
* authentication is conducted at runtime the <code>ProviderManager</code>.
* </p>
*
* @param authentication
*
* @return <code>true</code> if the implementation can more closely evaluate the
* <code>Authentication</code> class presented
*/
// 支持的Authentication(interface)
/**
|-Authentication
|--UsernamePassowrdAuthentication
|--CasAuthentication
|-- ........... **/ boolean supports(Class<?> authentication);
}

ProviderManager的authencate方法:

// 依次调用AuthencationProvider
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled(); // 遍历 AuthenticationProvider
for (AuthenticationProvider provider : getProviders()) {
// 当前的AuthenticationProvider是否支持Authentication
if (!provider.supports(toTest)) {
continue;
} if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
} try {
result = provider.authenticate(authentication); // 认证结果中如果不为null(验证成功),则遍历结束,拷贝认证后的结果到authentication对象
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
}
catch (InternalAuthenticationServiceException e) {
prepareException(e, authentication);
throw e;
}
catch (AuthenticationException e) {
lastException = e;
}
} if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = e;
}
} if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
} eventPublisher.publishAuthenticationSuccess(result);
return result;
} // Parent was null, or didn't authenticate (or throw an exception). if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
} prepareException(lastException, authentication); throw lastException;
}

授权

前面有filter处理了登录问题,接下来是否可访问指定资源的问题就由FilterSecurityInterceptor来处理了。而FilterSecurityInterceptor是用了AccessDecisionManager来进行鉴权。

来看看他干了什么

/**
* Method that is actually called by the filter chain. Simply delegates to the
* {@link #invoke(FilterInvocation)} method.
*
* @param request the servlet request
* @param response the servlet response
* @param chain the filter chain
*
* @throws IOException if the filter chain fails
* @throws ServletException if the filter chain fails
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
} 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 checking
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
else {
// first time this request being called, so perform security checking
if (fi.getRequest() != null && observeOncePerRequest) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
} // 调用前 // 该过程中会调用 AccessDecisionManager 来验证当前已认证成功的用户是否有权限访问该资源
InterceptorStatusToken token = super.beforeInvocation(fi); try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
} // 调用后
super.afterInvocation(token, null);
}
}

Spring Security探究之路之开始的更多相关文章

  1. spring security 关于 http.sessionManagement().maximumSessions(1);的探究

    1.前言 spring security 支持对session的管理 , http.sessionManagement().maximumSessions(1);的意思的开启session管理,ses ...

  2. spring security 4 filter 顺序及作用

    Spring Security 有两个作用:认证和授权 一.Srping security 4 filter 别名及顺序 spring security 4 标准filter别名和顺序,因为经常要用就 ...

  3. 从源码看Spring Security之采坑笔记(Spring Boot篇)

    一:唠嗑 鼓捣了两天的Spring Security,踩了不少坑.如果你在学Spring Security,恰好又是使用的Spring Boot,那么给我点个赞吧!这篇博客将会让你了解Spring S ...

  4. Spring Security OAuth2实现单点登录

    1.概述 在本教程中,我们将讨论如何使用 Spring Security OAuth 和 Spring Boot 实现 SSO(单点登录). 本示例将使用到三个独立应用 一个授权服务器(中央认证机制) ...

  5. Spring Security +Oauth2 +Spring boot 动态定义权限

    Oauth2介绍:Oauth2是为用户资源的授权定义了一个安全.开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的. 简单的来说,当用户登陆网站的时候,需要账号 ...

  6. Spring Security 入门原理及实战

    目录 从一个Spring Security的例子开始 创建不受保护的应用 加入spring security 保护应用 关闭security.basic ,使用form表单页面登录 角色-资源 访问控 ...

  7. Ajax登陆,使用Spring Security缓存跳转到登陆前的链接

    Spring Security缓存的应用之登陆后跳转到登录前源地址 什么意思? 用户访问网站,打开了一个链接:(origin url)起源链接 请求发送给服务器,服务器判断用户请求了受保护的资源. 由 ...

  8. 笔记43 Spring Security简介

    基于Spittr应用 一.Spring Security简介 Spring Security是为基于Spring的应用程序提供声明式安全保护的安全 性框架.Spring Security提供了完整的安 ...

  9. Spring Security原理篇(一) 启动原理

    1.概述 spring security有参考的中文翻译文档https://springcloud.cc/spring-security-zhcn.html 在学习spring security的时候 ...

随机推荐

  1. MySQL删除数据库或表(DROP DATABASE/table语句)

    DROP DATABASE [ IF EXISTS ] <数据库名> DROP table[ IF EXISTS ] <数据库表名> 语法说明如下: <数据库名>: ...

  2. Echart可视化学习(一)

    文档的源代码地址,需要的下载就可以了(访问密码:7567) https://url56.ctfile.com/f/34653256-527823386-04154f 正文: 创建需要的目录结构及文件 ...

  3. Maven自定义jar包名

    一.默认命名 <finalName>${project.artifactId}-${project.version}</finalName> 二.自定义包名 <build ...

  4. vue3.0+ts+setup语法糖props写法

    写法一 import defaultImg from '@/assets/images/defaultImg.png' const props = defineProps({ src: { type: ...

  5. c#多进程通讯,今天,它来了

    引言 在c#中,可能大多数人针对于多线程之间的通讯,是熟能生巧,对于AsyncLocal 和ThreadLocal以及各个静态类中支持线程之间传递的GetData和SetData方法都是信手拈来,那多 ...

  6. 网络协议学习笔记(七)流媒体协议和P2P协议

    概述 上一篇讲解了http和https的协议的相关的知识,现在我们谈一下流媒体协议和P2P协议. 流媒体协议:如何在直播里看到美女帅哥 最近直播比较火,很多人都喜欢看直播,那一个直播系统里面都有哪些组 ...

  7. 论文翻译:2020_FLGCNN: A novel fully convolutional neural network for end-to-end monaural speech enhancement with utterance-based objective functions

    论文地址:FLGCNN:一种新颖的全卷积神经网络,用于基于话语的目标函数的端到端单耳语音增强 论文代码:https://github.com/LXP-Never/FLGCCRN(非官方复现) 引用格式 ...

  8. 《剑指offer》面试题03. 数组中重复的数字

    问题描述 找出数组中重复的数字. 在一个长度为 n 的数组 nums 里的所有数字都在 0-n-1 的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次.请找出数组中 ...

  9. MRCTF2020 套娃

    MRCTF2020套娃 打开网页查看源代码 关于$_SERVER['QUERY_STRING']取值,例如: http://localhost/aaa/?p=222 $_SERVER['QUERY_S ...

  10. VictoriaMerics学习笔记(2):核心组件

    核心组件 1. 单机版 victoria-metrics-prod 单一二进制文件 读写都在一个节点上 作者推荐单机版 特性 merge方式配置 通过HTTP协议提供服务 内存限制(防止OOM) 使用 ...