上海网站建设报价表,做资源网站需要什么软件,肇庆cms建站系统,个人crm管理系统目录
1、SecurityContextHolder 核心类
2、SecurityContext 接口
3、Authentication 用户认证信息接口
4、GrantedAuthority 拥有权限接口
5、AuthenticationManager 身份认证管理器接口
6、ProviderManager 身份认证管理器的实现
7、AuthenticationProvider 特定类型的…目录
1、SecurityContextHolder 核心类
2、SecurityContext 接口
3、Authentication 用户认证信息接口
4、GrantedAuthority 拥有权限接口
5、AuthenticationManager 身份认证管理器接口
6、ProviderManager 身份认证管理器的实现
7、AuthenticationProvider 特定类型的身份验证接口
8、AuthenticationEntryPoint 身份验证方案接口
9、AbstractAuthenticationProcessingFilter 用户凭据验证过滤器 // Spring Security 基于 Servlet 的身份验证和身份验证的基本构件
SecurityContextHolder类——用来存储身份验证详细信息。//验证流程核心类SecurityContext接口——从SecurityContextHolder 中获取包含当前认证用户的身份验证信息。Authentication接口 ——该对象可以通过 AuthenticationManager 进行赋值作为用户身份验证的凭据然后通过 SecurityContext 获取 Authentication 的详细信息。GrantedAuthority接口——用于对用户的授权。AuthenticationManager接口——用于定义 Spring Security 过滤器如何执行身份验证的API。ProviderManager类——AuthenticationManager 最常见的实现。//常规实现AuthenticationProvider接口——提供给ProviderManager用于执行特定类型的身份验证。//身份验证的具体细节由xxxProvider提供AuthenticationEntryPoint接口——用于从客户端请求用户凭据例如重定向到登录页面发送WWW-Authenticate响应等AbstractAuthenticationProcessingFilter抽象类——用于身份验证的基本过滤器。通过该类可以很好地了解身份验证流程及各个部分如何协同工作的过程。//所有身份验证的过滤器都会实现该类
1、SecurityContextHolder 核心类 SecurityContextHolder 类是 Spring Security 身份验证模型的核心 在该类中包含了存储用户认证信息的上下文 SecurityContext。 下边是一个对象之间的包含关系图它很重要对于理解 Spring Security 中的对象关系非常有用 SecurityContextHolder 用来存储身份验证详细信息。Spring Security 并不关心SecurityContextHolder 是如何填充的。如果其中包含值则使用该值作为当前通过认证的用户信息。//只要SecurityContextHolder有值就会被使用 所以指示用户已被认证的最简单方法就是直接设置 SecurityContextHolder
SecurityContext context SecurityContextHolder.createEmptyContext(); //1-创建Authentication - Token接口
Authentication authentication new TestingAuthenticationToken(username, password, ROLE_USER);
//2-设置用户信息
context.setAuthentication(authentication);SecurityContextHolder.setContext(context); 使用 TestingAuthenticationToken是因为它非常简单。也可以使用 UsernamePasswordAuthenticationTokenUserDetails、密码、权限等。 // 定义一个Authentication - 放入SecurityContext - 放入 SecurityContextHolder 在 SecurityContextHolder 上设置完 SecurityContext。Spring Security 会使用此信息进行授权。如果要获取已验证用户的有关信息可以通过SecurityContextHolder获得
//1-获取Security上下文
SecurityContext context SecurityContextHolder.getContext();//2-获取用户通过身份验证的对象
Authentication authentication context.getAuthentication();//-通过Authentication获取用户名称密码方式
String username authentication.getName();
//-通过Authentication被认证用户的身份信息。
Object principal authentication.getPrincipal();
//-通过Authentication用户权限信息
Collection? extends GrantedAuthority authorities authentication.getAuthorities(); 默认情况下SecurityContextHolder 使用 ThreadLocal 来存储 SecurityContext这意味着 SecurityContext 对于同一线程中的方法总是可用的并不需要把 SecurityContext 作为参数显式地传递给这些方法。//SecurityContext 属于线程私有如果我们不想显示传参也可以这样用 //包含SecurityContext的成员变量private static SecurityContextHolderStrategy strategy; //初始化方法private static void initialize() {//1-如果没有指定mode,使用MODE_THREADLOCALif (!StringUtils.hasText(strategyName)) { strategyName MODE_THREADLOCAL;}if (strategyName.equals(MODE_THREADLOCAL)) {//2-默认策略strategy new ThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {strategy new InheritableThreadLocalSecurityContextHolderStrategy();} else if (strategyName.equals(MODE_GLOBAL)) {strategy new GlobalSecurityContextHolderStrategy();} else {try {Class? clazz Class.forName(strategyName);Constructor? customStrategy clazz.getConstructor();strategy (SecurityContextHolderStrategy)customStrategy.newInstance();} catch (Exception var2) {ReflectionUtils.handleReflectionException(var2);}}initializeCount;}// MODE_THREADLOCAL策略final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {// 使用ThreadLocal保存SecurityContextprivate static final ThreadLocalSecurityContext contextHolder new ThreadLocal();ThreadLocalSecurityContextHolderStrategy() {}//省略...} 因为使用了 ThreadLocal 存储值如果处理完当前用户请求后需要清除线程中的ThreadLocal避免内存泄漏。Spring Security 的FilterChainProxy会确保SecurityContext总是被清除。//FilterChainProxy是一个过滤器链的总代理在总体架构那篇文章中提到过 //在FilterChainProxy中的doFilter方法中清除public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {boolean clearContext request.getAttribute(FILTER_APPLIED) null;if (!clearContext) {this.doFilterInternal(request, response, chain);} else {try {request.setAttribute(FILTER_APPLIED, Boolean.TRUE);this.doFilterInternal(request, response, chain);} catch (RequestRejectedException var9) {this.requestRejectedHandler.handle((HttpServletRequest)request, (HttpServletResponse)response, var9);} finally {//处理完一个用户请求时SecurityContext在这里被清除SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED);}}}
2、SecurityContext 接口 SecurityContext 从 SecurityContextHolder 中获取。SecurityContext 包含一个 Authentication 对象。
// SecurityContext接口
public interface SecurityContext extends Serializable {//接口很简单只包含对Authentication的get和set方法Authentication getAuthentication();void setAuthentication(Authentication var1);
}SecurityContext 的实现类 SecurityContextImpl 也很简单也就是对 Authentication 对象 get 和 set 方法的实现所以重点还是 Authentication 对象。
3、Authentication 用户认证信息接口 在 Spring Security 中Authentication 接口有两个主要用途
作为 AuthenticationManager 对象 authenticate方法的入参用于对用户进行身份验证。在此场景中isAuthenticated() 返回 false。获取已经过身份验证的用户信息可以从 SecurityContext 中获取当前的用户信息。 一旦请求通过了AuthenticationManager#authenticateAuthentication 通常会存储在线程本地的 SecurityContext 中由 SecurityContextHolder 进行管理。//Authentication-SecurityContext 注意除非 Authentication 将 isAuthenticated 属性设置为 true否则安全拦截器仍然会对其进行身份验证。 Authentication 接口详情//每个方法的注释都值得好好去读
public interface Authentication extends Principal, Serializable {//1-权限由AuthenticationManager设置用于指示已授予主体的权限。认证后不能为空即无权限Collection? extends GrantedAuthority getAuthorities();//2-密码证明委托人正确的凭据。通常是密码Object getCredentials();//3-用户详情存储有关身份验证请求的其他详细信息。如IP地址证书序列号等Object getDetails();//4-用户名被认证主体的身份。通常是用户名许多身份验证提供程序将创建UserDetails对象作为主体Object getPrincipal();//用于指示AbstractSecurityInterceptor是否应该向AuthenticationManager提供身份验证令牌。//如果令牌已经过身份验证并且AbstractSecurityInterceptor不需要再次向AuthenticationManager提供令牌以进行重新身份验证则为true。boolean isAuthenticated();//如果令牌应该被信任(这可能导致异常)则为true;如果令牌不应该被信任则为falsevoid setAuthenticated(boolean var1) throws IllegalArgumentException;
} 所以综合上述Authentication 包含三方面信息//总结起来就是用户信息权限
Principal用户主体信息该信息通常是 UserDetails 的一个实例。Credentials认证凭据通常是密码。在许多情况下用户通过身份验证后会被ProviderManager清除该属性以确保凭据不会泄露。Authorities用户权限由 GrantedAuthority 进行授予。
4、GrantedAuthority 拥有权限接口 表示授予身份验证对象的权限。该接口的内容提供给授权管理器AuthorizationManager使用以确定身份验证是否有权访问特定对象。//保存的是用户角色
//GrantedAuthority接口
public interface GrantedAuthority extends Serializable {String getAuthority(); //返回的是一个字符串 String role
} // 所以GrantedAuthority这个玩意是用来授权的存储已认证用户具备的访问权限。 通过 Authentication.getAuthorities() 方法可以获取 GrantedAuthority 的实例。此方法提供了 GrantedAuthority 对象的集合。毫无疑问GrantedAuthority 是授予用户的权限。这些权限通常是“角色”信息例如 ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR。这些角色将配置给 Web 授权、方法授权和域对象授权使用。当使用基于用户名/密码的身份验证时GrantedAuthority 实例通常由 UserDetailsService 加载。 通常GrantedAuthority 对象是应用程序范围的权限。它们并不特定于给定的域对象。因此不太可能使用 GrantedAuthority 来表示一个对象的具体权限因为如果有数千个这样的权限将很快耗尽内存或者会导致应用程序花费很长时间来进行验证。// 其意思是说GrantedAuthority 中的权限是一类权限角色而不是某个具体的功能比如管理员权限能操作所有模块而所有模块的功能汇聚在一起可能有好几百个。
5、AuthenticationManager 身份认证管理器接口 AuthenticationManager 定义 Spring Security 的过滤器如何执行身份验证的 API。用于处理身份验证请求。
public interface AuthenticationManager {//尝试对传递的身份验证对象进行身份验证//如果成功则返回一个完全填充的身份验证对象(包括授予的权限)。Authentication authenticate(Authentication var1) throws AuthenticationException;
} Spring Security 的 Filters 实例会调用 AuthenticationManager 获取在 SecurityContextHolder上设置的 Authentication 对象进行身份验证。 如果直接设置 SecurityContextHolder就不需要使用 AuthenticationManager。//直接填充身份信息跳过认证步骤由此可以见所有的认证流程也只是去SecurityContextHolder中设置一个Authentication而已 AuthenticationManager 可以有很多种实现其中最常见的实现是 ProviderManager。
6、ProviderManager 身份认证管理器的实现 ProviderManager 是 AuthenticationManager 最常用的实现。ProviderManager 对用户的认证委托给一个 AuthenticationProvider 实例的列表。
//具体进行认证操作的实例列表
private ListAuthenticationProvider providers; 每个 AuthenticationProvider 都有机会表明身份验证应该是成功的、失败的或者表明它不能做出决定并允许下游的 AuthenticationProvider 做出决定。 如果配置的 AuthenticationProvider 实例都不能进行身份验证则身份验证失败并产生一个ProviderNotFoundException这是一个特殊的 AuthenticationException表明 ProviderManager 没有配置支持此 Authentication 对象认证的 Provider 类型。 实际中每个 AuthenticationProvider 都会去执行一个特定类型的身份验证。例如一个AuthenticationProvider 可能能够验证用户名/密码而另一个 AuthenticationProvider 可能能够验证 SAML 断言。Spring Security 同时支持多种身份验证类型并且只公开一个通用的 AuthenticationManager Bean。//对应各种功能的过滤器 ProviderManager 还允许配置一个父类 AuthenticationManager可选它在没有AuthenticationProvider 可以执行身份验证的情况下被使用。父类可以是任何类型的 AuthenticationManager通常也是一个 ProviderManager 实例。 多个 ProviderManager 实例可以共享同一个父 AuthenticationManager。该方式在存在多个 SecurityFilterChain 的场景中比较常见这些安全过滤器链有一些共同的身份验证(共享的父AuthenticationManager)但也有不同的身份验证机制不同的 ProviderManager 实例。 默认情况下ProviderManager 会尝试从成功的身份验证请求返回的 Authentication 对象中清除敏感的凭据信息防止信息如密码在 HttpSession 中保留的时间超过所需的时间。// Authentication 认证成功后ProviderManager 会清除其中的认证凭据 // 因为ProviderManager会清除Authentication认证凭据所以缓存Authentication信息时应该注意此问题。 解决缓存问题的两种方案创建 Authentication 对象的副本或者禁用 ProviderManager 上的 eraseCredentialsAfterAuthentication 属性。 【ProviderManager.authenticate 源码分析】
//执行认证的AuthenticationProvider列表
private ListAuthenticationProvider providers; //认证流程
public Authentication authenticate(Authentication authentication) throws AuthenticationException {Class? extends Authentication toTest authentication.getClass();AuthenticationException lastException null;AuthenticationException parentException null;Authentication result null;Authentication parentResult null;int currentPosition 0;int size this.providers.size();//1-获取Provider列表迭代器Iterator var9 this.getProviders().iterator(); //2-开始循环遍历Provider列表while(var9.hasNext()) { AuthenticationProvider provider (AuthenticationProvider)var9.next();if (provider.supports(toTest)) {//省略...try {//3-使用其中一个provider进行身份验证result provider.authenticate(authentication);if (result ! null) {this.copyDetails(authentication, result);break;}} catch (InternalAuthenticationServiceException | AccountStatusException var14) {this.prepareException(var14, authentication);throw var14;} catch (AuthenticationException var15) {lastException var15;}}}//省略...(这里省略了付实例的认证也很简单可查看源码)//4-如果认证成功在这里擦除密码/认证凭据等信息if (result ! null) { if (this.eraseCredentialsAfterAuthentication result instanceof CredentialsContainer) {((CredentialsContainer)result).eraseCredentials(); //擦除认证凭据}if (parentResult null) {this.eventPublisher.publishAuthenticationSuccess(result);}return result;} else {//5-抛出providerNotFound异常if (lastException null) {lastException new ProviderNotFoundException(this.messages.getMessage(ProviderManager.providerNotFound, new Object[]{toTest.getName()}, No AuthenticationProvider found for {0}));}if (parentException null) {this.prepareException((AuthenticationException)lastException, authentication);}throw lastException;}}
7、AuthenticationProvider 特定类型的身份验证接口 用于处理特定身份验证的统一接口。 通常可以将多个 AuthenticationProvider 实例注入到 ProviderManager 中。每个AuthenticationProvider 执行特定类型的身份验证。例如DaoAuthenticationProvider 支持基于用户名/密码的身份验证而 JwtAuthenticationProvider 支持验证 JWT 令牌。
public interface AuthenticationProvider {//执行身份验证Authentication authenticate(Authentication var1) throws AuthenticationException;//如果此AuthenticationProvider支持指定的Authentication对象则返回true。boolean supports(Class? var1);
}
8、AuthenticationEntryPoint 身份验证方案接口 AuthenticationEntryPoint 作用由 ExceptionTranslationFilter 用于启动一个身份验证方案。
public interface AuthenticationEntryPoint {//启动认证方案void commence(HttpServletRequest var1, HttpServletResponse var2, AuthenticationException var3) throws IOException, ServletException;
} ExceptionTranslationFilter 将填充 AbstractAuthenticationProcessingFilter 的 HttpSession 属性。在调用此方法之前使用所请求的目标URL。 实现根据需要修改 ServletResponse 上的报文头以启动身份验证过程。AuthenticationEntryPoint 的实现可能会执行重定向到登录页面、使用 WWW-Authenticate 头响应或采取其他操作。
9、AbstractAuthenticationProcessingFilter 用户凭据验证过滤器 AbstractAuthenticationProcessingFilter 用作验证用户凭据的基本过滤器。在对凭证进行身份验证之前Spring Security 通常会使用 AuthenticationEntryPoint 来请求凭证。 【AbstractAuthenticationProcessingFilter.doFilter 源码】//总流程 //身份认证过滤器的处理流程private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {if (!this.requiresAuthentication(request, response)) {chain.doFilter(request, response);} else {try {//1- 尝试进行身份验证Authentication authenticationResult this.attemptAuthentication(request, response);if (authenticationResult null) {return;}//2-如果验证成功创建新会话更新sessionIdthis.sessionStrategy.onAuthentication(authenticationResult, request, response);if (this.continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}//3-调用认证成功后处理流程this.successfulAuthentication(request, response, chain, authenticationResult);} catch (InternalAuthenticationServiceException var5) {this.logger.error(An internal error occurred while trying to authenticate the user., var5);//4-调用认证失败后处理流程this.unsuccessfulAuthentication(request, response, var5);} catch (AuthenticationException var6) {//4-调用认证失败后处理流程this.unsuccessfulAuthentication(request, response, var6);}}} 接下来AbstractAuthenticationProcessingFilter 可以对提交给它的任何身份验证请求进行身份验证。 //过滤器中尝试进行身份验证的抽象方法public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;//一个具体实现类UsernamePasswordAuthenticationFilter的实现逻辑public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly !request.getMethod().equals(POST)) {throw new AuthenticationServiceException(Authentication method not supported: request.getMethod());} else {String username this.obtainUsername(request);username username ! null ? username : ;username username.trim();String password this.obtainPassword(request);password password ! null ? password : ;//使用用户名和密码创建一个Authentication对象UsernamePasswordAuthenticationToken authRequest new UsernamePasswordAuthenticationToken(username, password);this.setDetails(request, authRequest);//调用特定的认证管理器进行认证通常为ProviderManagerreturn this.getAuthenticationManager().authenticate(authRequest);}} AbstractAuthenticationProcessingFilter 进行认证的流程图示 当用户提交凭据时AbstractAuthenticationProcessingFilter 从 HttpServletRequest 创建一个要进行身份验证的 Authentication 对象。创建的身份验证类型取决于AbstractAuthenticationProcessingFilter 的子类。例如UsernamePasswordAuthenticationFilter 从 HttpServletRequest 中提交的用户名和密码中创建 UsernamePasswordAuthenticationToken。//看上边的源码展示 接下来将 Authentication 对象 传递到 AuthenticationManager 中进行身份验证。 如果身份验证失败则执行 Failure 流程//失败流程有许多的拓展点 1调用 SecurityContextHolder.clearContext 方法清除 Security 上下文。//ThreadLocal.remove 2调用 RememberMeServices.loginFail 方法。有两个实现类 NullRememberMe 和 RememberMe。 //RememberMe会失效Cookie 3调用 AuthenticationFailureHandler.onAuthenticationFailure 方法。//抛出错误信息或者重定向 【AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication 源码】 //认证失败处理流程protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {//1-清除SecurityContextHolderSecurityContextHolder.clearContext();this.logger.trace(Failed to process authentication request, failed);this.logger.trace(Cleared SecurityContextHolder);this.logger.trace(Handling authentication failure);//2-调用RememberMeServices.loginFailthis.rememberMeServices.loginFail(request, response);//3-调用AuthenticationFailureHandlerthis.failureHandler.onAuthenticationFailure(request, response, failed);} 如果身份验证成功则执行 Success 流程 1SessionAuthenticationStrategy.onAuthentication 会收到新的登录通知。 2在 SecurityContextHolder 中设置通过认证的用户信息方便后续取用。 3调用 RememberMeServices.loginSuccess 方法。//通过此方法可自定义成功后的处理方式。 4调用 ApplicationEventPublisher.publishEvent 方法发布一个InteractiveAuthenticationSuccessEvent 事件。//用户可通过监听此事件来做进一步操作 5调用 AuthenticationSuccessHandler.onAuthenticationSuccess 方法。//处理url重定向等 【AbstractAuthenticationProcessingFilter.successfulAuthentication 源码】 //认证成功处理流程protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {//1-在 SecurityContextHolder 中设置通过认证的用户信息方便后续取用SecurityContextHolder.getContext().setAuthentication(authResult);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format(Set SecurityContextHolder to %s, authResult));}//2-调用 RememberMeServices.loginSuccess 方法this.rememberMeServices.loginSuccess(request, response, authResult);if (this.eventPublisher ! null) {//3-发布认证成功事件this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));}//4-调用AuthenticationSuccessHandler.onAuthenticationSuccess方法this.successHandler.onAuthenticationSuccess(request, response, authResult);} 最后熟知用户的认证流程对熟练使用 Spring Security 非常重要。需要多学习多总结。 至此全文到此结束。