SpringSecurity6认证和授权的原理

2023年 7月 26日 31.6k 0

一、Spring Security简介

Spring Security是一个基于Spring框架的安全解决方案,提供了认证和授权等安全方面的大服务,包括身份认证和权限处理两大服务。Spring Security的实现依赖于大量的过滤器,采用责任链模式对请求请求不同的过滤处理。在日常使用中,Spring Security已经实现了基于表单的登录认证和授权模式,只需简单的配置,即可做到拿来即用。当然Spring Security也提供了灵活的其他认证入口,通过实现其暴漏出来的接口即可自定义自己的登录认证和授权方法。

  • 认证(Authentication):是Spring Security识别当前登录用户身份的主要方式。
  • 授权(Authorization):是Spring Security对当前认证用户权限管理的主要方式。

二、Spring Security框架中认证流程中几个非常重要的类

1、 FilterChainProxy

这是一个Spring Security框架中非常重要的类,它用于维护一组过滤器连,属于Servlet级别的过滤器。所有的请求在进入WEB容器之前都会经过该过滤器,该过滤器会对请求进行不同程度的拦截和处理。FilterChainProxy根据不同的请求获取对应的过滤器链组,之后按照顺序执行该请求对应的过滤器链组中的每一个过滤器的逻辑对该请求进行处理,直到所有的过滤器都执行完成。FilterChainProxy实际上是一个Filter,它的doFilter方法最终会调用自己内部的doFilterInternal方法。

public class FilterChainProxy extends GenericFilterBean {

	private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		……省略部分代码……
		
		// 根据请求类型,获取请求下的所有的过滤器
		List filters = getFilters(firewallRequest);

		// 循环执行匹配到该请求下的所有的过滤器
		FilterChain reset = (req, res) -> {
			// Deactivate path stripping as we exit the security filter chain
			firewallRequest.reset();
			chain.doFilter(req, res);
		};
		this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
	}
}

2、 AbstractAuthenticationProcessingFilter

这个是认证过程中抽出来的抽象类,自定义的过滤器可直接实现这个抽象类自定义过滤方式,UsernamePasswordAuthenticationFilter就是直接实现的这个抽象类。AbstractAuthenticationProcessingFilter实际上是一个Filter,它的doFilter方法最终会调用自己内部的attemptAuthentication方法。这是一个抽象方法,需要子类实现它,UsernamePasswordAuthenticationFilter就是直接实现该抽象类做到,实现了attemptAuthentication方法。

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean  
implements ApplicationEventPublisherAware, MessageSourceAware {
@Override
	public 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 {
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);
			return;
		}
		try {
			// 调用内部的attemptAuthentication方法,该方法是抽象方法。需要子类实现。
			Authentication authenticationResult = attemptAuthentication(request, response);
			if (authenticationResult == null) {
				// return immediately as subclass has indicated that it hasn't completed
				return;
			}
			this.sessionStrategy.onAuthentication(authenticationResult, request, response);
			// Authentication success
			if (this.continueChainBeforeSuccessfulAuthentication) {
				chain.doFilter(request, response);
			}
			// 认证成功之后会调用该方法处理认证成功之后的逻辑。
			successfulAuthentication(request, response, chain, authenticationResult);
		}
		catch (InternalAuthenticationServiceException failed) {
			this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
			unsuccessfulAuthentication(request, response, failed);
		}
		catch (AuthenticationException ex) {
			// Authentication failed
			// 认证失败会调用该方法处理认证失败之后的逻辑。
			unsuccessfulAuthentication(request, response, ex);
		}
	}
}

3、 UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilteAbstractAuthenticationProcessingFilter的直接实现,也是Spring Security表单登录的默认实现过滤器,主要是将表单登录的所需要的usernamepassword从request中获取出来封装成还未认证过的UsernamePasswordAuthenticationToken,它实现了AbstractAuthenticationProcessingFilter的抽象方法attemptAuthentication方法,最终将登录逻辑的具体实现委托给AuthenticationManager去实现,而AuthenticationManager是一个接口,里面只有一个抽象方法authenticate,必须由子类实现,而最直接的实现该接口的类就是ProviderManager

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
	@Override
	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());
		}
		// 从请求中获取username参数
		String username = obtainUsername(request);
		username = (username != null) ? username.trim() : "";
		// 从请求中获取password参数
		String password = obtainPassword(request);
		password = (password != null) ? password : "";
		// 构造一个没有完全认证的AuthenticationToken,最主要的是就是用来存放未认证之前的用户信息的。未认证之前,AuthenticationToken里面的authenticated属性为false,认证通过直接会被设置为true。
		UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
				password);
		// Allow subclasses to set the "details" property
		// 实际上就是保存了一下用户的详细信息
		setDetails(request, authRequest);
		// 调用AuthenticationManager的authenticate方法处理认证逻辑,实际上会调到具体的子类实现上。
		return this.getAuthenticationManager().authenticate(authRequest);
	}
}
4、AbstractAuthenticationToken

AbstractAuthenticationToken是一个用来存放用户信息的抽象类,比如表单登录的usernamepassword就会被存放在该类的实现中,而它的直接实现就是UsernamePasswordAuthenticationTokenAbstractAuthenticationToken里面有几个重要的属性:

public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
	// 当前登录用户的权限集合
	private final Collection authorities;  
	// 用户信息的详细信息
	private Object details;  
	// 当前登录用户是否完全认证。 未认证之前,authenticated属性为false,认证通过直接会被设置为true。
	private boolean authenticated = false;
}

5、 UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationTokenAbstractAuthenticationToken的直接实现,也是Spring Security表单登录的默认实现用来存放用户信息的类。该类的作用就是存放未认证之前的用户信息,也就是表单提交的username和password。

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
	// 在父类的基础之上,UsernamePasswordAuthenticationToken增加了该属性信息,源码实现的时候,未认证之前该属性代表的是username,认证之后存放的是UserDetails
	private final Object principal;  
	// 在父类的基础之上,UsernamePasswordAuthenticationToken增加了该属性信息,源码实现的时候,未认证之前该属性代表的是password,认证之后存放的是认证主体Authentication的一些信息
	private Object credentials;
}
6、 AuthenticationManager

AuthenticationManager是实际上用来做认证的接口,内部就一个authenticate抽象方法,子类必须实现它,ProviderManagerAuthenticationManager的最直接的实现。

public interface AuthenticationManager {
	// 实际上的认证入口
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

7、 ProviderManager

ProviderManagerAuthenticationManager的最直接的实现,实现了authenticate抽象方法,也是Spring Security表单登录的默认实现认证相关的类,认证的处理逻辑就是在这个里面处理的。而在 ProviderManager又把认证逻辑委托给了对应的AbstractUserDetailsAuthenticationProvider,他是实际上干活的类,认证逻辑最终在这里执行。

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论