从原码的角度分析springsecurity登录过程

当我们要访问我们的一些资源时,要先执行 一些过滤器,spring security就只通过下图所示的过滤器链实现的
在这里插入图片描述
当在项目中添加了springsecurity的依赖后,springboot就会自动的把这些filter添加进去,上图中绿色的过滤器是可以自己配置的(usernamepasswordfilter就是表单登录的过滤器),蓝色和橙色的过滤器是必须执行的

我们来看一下实现登录验证的usernamepasswordfilte是如何工作的
首先会进入usernamepasswordfilte(未认证的authentication)然后进入ProviderManager寻找合适的provider并调用provider.authenticate(authentication);再然后就是UserDetailsService接口的实现类,最后回到UsernamePasswordAuthenticationFilter,最后就得到一个认证的authentication

UsernamePasswordAuthenticationFilter.java

// 继承了AbstractAuthenticationProcessingFilter
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException {
        // 认证请求的方式必须为POST
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        // 获取用户名
        String username = obtainUsername(request);
        // 获取密码
        String password = obtainPassword(request);

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

        if (password == null) {
            password = "";
        }
        // 用户名去空白
        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

AbstractAuthenticationProcessingFilter.java

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
    
    // 过滤器doFilter方法
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        /*
         * 判断当前filter是否可以处理当前请求,若不行,则交给下一个filter去处理。
         */
        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);

            return;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Request is to process authentication");
        }

        Authentication authResult;

        try {
            // 很关键!!!调用了子类(UsernamePasswordAuthenticationFilter)的方法
            authResult = attemptAuthentication(request, response);
            if (authResult == null) {
                // return immediately as subclass has indicated that it hasn't completed
                // authentication
                return;
            }
            // 最终认证成功后,会处理一些与session相关的方法(比如将认证信息存到session等操作)。
            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);
        }
        /*
         * 最终认证成功后的相关回调方法,主要将当前的认证信息放到SecurityContextHolder中
         * 并调用成功处理器做相应的操作。
         */
        successfulAuthentication(request, response, chain, authResult);
    }
}

UsernamePasswordAuthenticationFilter继承了AbstractAuthenticationProcessingFilter,会先执行父类AbstractAuthenticationProcessingFilter,我们主要看dofilter()方法
处理流程就是
1.首先判断当前filter是否可以处理当前请求,若不行,则交给下一个filter去处理。

 // 判断当前filter是否可以处理当前请求,若不行,则交给下一个filter去处理
        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
            return;
        }

2.调用了子类UsernamePasswordAuthenticationFilter的attemptAuthentication(request, response)方法

 authResult = attemptAuthentication(request, response);

3.对认证的结果进行处理

 // 最终认证成功后,会处理一些与session相关的方法(比如将认证信息存到session等操作)。
            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);
        }
        /*
         * 最终认证成功后的相关回调方法,主要将当前的认证信息放到SecurityContextHolder中
         * 并调用成功处理器做相应的操作。
         */
        successfulAuthentication(request, response, chain, authResult);
    }
}

子类UsernamePasswordAuthenticationFilter的处理流程是

1.检查请求方式是不是POST方式

        // 认证请求的方式必须为POST
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
       

2.获得用户名和密码,对用户名和密码进行处理

 String username = obtainUsername(request);
		String password = obtainPassword(request);

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

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

		username = username.trim();

3.封装成UsernamePasswordAuthenticationToken,因为此时还没有认证,所以没有权限,false未认证

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

/*public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}*/

4.最后重要一步,进行认证

return this.getAuthenticationManager().authenticate(authRequest);

调用的是由ProviderManager的authenticate方法,因为ProviderManager实现了AuthenticationManager

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
		InitializingBean {
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();

		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
				continue;
			}

			if (debug) {
				logger.debug("Authentication attempt using "
						+ provider.getClass().getName());
			}

			try {
				result = provider.authenticate(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;
	}

ProviderManager的处理流程是
1.遍历所有的provider,看是否支持当前的Authentication

for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
				continue;
			}
}

2.如何找到了合适的provider,就调用provider.authenticate(authentication)

try {
	result = provider.authenticate(authentication);

	if (result != null) {
		copyDetails(authentication, result);
		break;
	}
}

result = provider.authenticate(authentication);这一步是调用了DaoAuthenticationProvider的retrieveUser方法

protected final UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		UserDetails loadedUser;

		try {
			loadedUser = this.getUserDetailsService().loadUserByUsername(username);
		}
		catch (UsernameNotFoundException notFound) {
			if (authentication.getCredentials() != null) {
				String presentedPassword = authentication.getCredentials().toString();
				passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
						presentedPassword, null);
			}
			throw notFound;
		}
		catch (Exception repositoryProblem) {
			throw new InternalAuthenticationServiceException(
					repositoryProblem.getMessage(), repositoryProblem);
		}

		if (loadedUser == null) {
			throw new InternalAuthenticationServiceException(
					"UserDetailsService returned null, which is an interface contract violation");
		}
		return loadedUser;
	}

retrieveUser中最关键的loadedUser=this.getUserDetailsService().loadUserByUsername(username);
这一步就是在调用我们自己的业务代码
比如:


@Service
public class UserServiceImpl implements UserService, UserDetailsService {

	@Autowired
	private UserRepository userRepository;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
		return userRepository.findByUsername(username);
	}
}

类之间的调用顺序

UsernamePasswordAuthenticationFilter
Authentication
AuthenticationManager
AuthenticationProvider
UserDetailsService
// 回到起点进行后续操作,比如缓存认证信息到session和调用成功后的处理器等等
UsernamePasswordAuthenticationFilter 

疑问:

1、接口login在哪定义的?
SpringSecurity内置的,并且只能为POST

public UsernamePasswordAuthenticationFilter() {
    super(new AntPathRequestMatcher("/login", "POST"));
}

2、用户名username和密码password在哪接收的?
名称不能变,必须是username和password

public class UsernamePasswordAuthenticationFilter extends
      AbstractAuthenticationProcessingFilter {
   // ~ Static fields/initializers
   // =====================================================================================

   public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
   public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
}

参考文章https://www.jianshu.com/p/a65f883de0c1

猜你喜欢

转载自blog.csdn.net/victoyr/article/details/88385631