spring security 用户认证原理

目录

一、从过滤器链开始

二、获取用户信息

三、用户名和密码认证流程

四、构建成功的认证对象

五、认证信息放入SecurityContext


一、从过滤器链开始

当我们执行认证逻辑,在获取用户信息时,可打一个断点,跟踪程序的调用链

在程序的调用链中,找到一个 StandardWrapperValve 的记录,StandardWrapperValve.class 类位于 tomcat 核心包中,跟踪到这个调用位置(StandardWrapperValve#invoke),可以看到过滤器链的调用

二、获取用户信息

在调用链的一系列过滤器中,找到 UsernamePasswordAuthenticationFilter.calss(用户名和密码认证过滤器)

用户名和密码认证过滤器继承了 AbstractAuthenticationProcessingFilter.calss,该过滤器的 #doFilter 方法中,调用了用户名和密码认证过滤器中用户认证的逻辑

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
				// authentication
				return;
			}
			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 failed 认证失败
			unsuccessfulAuthentication(request, response, failed);

			return;
		}

		// Authentication success 认证成功
		if (continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}
        // 扩展点 -> AuthenticationSuccessHandler
		successfulAuthentication(request, response, chain, authResult);
	}

调用 attemptAuthentication(request, response) 抽象方法

UsernamePasswordAuthenticationFilter#attemptAuthentication 实现了该抽象方法

public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		// 1-必须为 post 方法
        if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}
        // 2-解析用户名和密码 -> 固定解析参数名称 'username','password'
		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();
        // 3-封装到认证的token,此时认证状态为 false -> setAuthenticated(false); 
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
        // 4-调用认证管理器进行认证 -> 认证核心逻辑
		return this.getAuthenticationManager().authenticate(authRequest);
	}

方法 this.getAuthenticationManager().authenticate(authRequest); 调用认证管理器的具体实现进行权限验证

具体认证管理器的实现逻辑为 ProviderManager.class#authenticate

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;
		boolean debug = logger.isDebugEnabled();
        // providers 列表循环
		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
		
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值