用户名密码认证是最常用的认证方式之一,本篇博客就来探讨一下Spring Security基于表单的登录是如何工作的。

spring security记住密码数据库实现 spring security 用户名密码_spring

从图开始,经过了之前的两篇博客,图中的几个角色都是见过认识的。

首先,客户端向没有授权的资源例如/private路径发起一个没有经过身份验证的请求,简单来说就是没有登录过,去请求一个需要权限的路径,这时候肯定是请求不了的,所以到了FilterSecurityIntercepter这个Filter的时候,FilterSecurityIntercepter会抛出一个AccessDeniedException异常,这个异常在ExceptionTranslationFilter中被捕获,ExceptionTranslationFilter捕获到这个异常之后就会去调用LoginUrlAuthenticationEntryPoint,重定向到登录页。

跳转到登录页面之后,我们就要输入用户名密码,然后进行登录

spring security记住密码数据库实现 spring security 用户名密码_表单_02

以上是登录的流程,用户点击登录按钮,发送一个登录请求,请求被UsernamePasswordAuthenticationFilter拦截。

UsernamePasswordAuthenticationFilter创建一个UsernamePasswordAuthenticationToken,UsernamePasswordAuthenticationToken是Authentication的实现类。

再接着将这个Authentication传入AuthenticationManager进行登录认证,之后的这些流程上一篇AbstractAuthenticationProcessingFilter都说过。

UsernamePasswordAuthenticationFilter继承自AbstractAuthenticationProcessingFilter,需要关注的地方就是attemptAuthentication这个抽象方法。

public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}

		//获取request中的username和password参数
		String username = obtainUsername(request);
		String password = obtainPassword(request);

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

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

		username = username.trim();

		//初始化构建一个UsernamePasswordAuthenticationToken,传入用户名密码,设置成principal和credentials
		//并且状态是未认证,且没有角色
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		//设置请求的详细信息
		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);

		//执行认证
		return this.getAuthenticationManager().authenticate(authRequest);
	}

从代码中看到,从request中的用户名密码参数构建了一个Authentication,接着执行认证。

认证的过程交给了AuthenticationManager,这个上一篇都说到过,包括其具体设置的过程,实现是一个ProviderManager。

而从上一篇博客中可以看出,现在只需要关注ProviderManager中的AuthenticationProvider的列表。

在这里这个AuthenticationProvider先放一放,按照文档的节奏来。

再来看一看另外一个认证,基本认证,Basic Authentication

spring security记住密码数据库实现 spring security 用户名密码_spring_03

和表单认证不同,Basic Authentication的场景是如果我们不需要表单的情况下,如图,基本流程类似,不同的就是Authentication EntryPoint

public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException {
		response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
		response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
	}

两种认证方式讲完,结束