spring security,部分源码理解。

296 阅读3分钟

用掘金-Markdown 编辑器写文章

我相信有很多人和我一样,刚刚开始接触spring security 有一大堆问号。
如:
为什么我登录默认是login,等出默认是logout。
为什么增加的filter有时候是链式传递,有时候却又到某一部阶段不继续下去。
为什么默认登录成功之后可以继续访问登录之前的页面呢?存在哪里呢?
······
我相信这些问题你们都有过疑问。我试过去搜索,然后看文档。但是没说全啊。怎么办,只能看源码了啊。
看了源码让我这两天的的疑惑统统解开。框架好就好在开箱即用,只配置一点内容就可以实现,但是我这种人喜欢
刨根究底,封装起来反而让我疑惑,头大。好了,话不多说,让我们来一步步看下来吧。
当我们导入了spring security的包之后为什么访问url直接就生效
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
跳转到这个页面了?

其实它有一个默认的配置。
protected void configure(HttpSecurity http) throws Exception {
	http
		.authorizeRequests()
		.anyRequest().authenticated()
		.and()
		.formLogin()
		.and()
		.httpBasic();
}
那为什么这个配置写了就会跳转到/login呢?
当我们身份校验不通过,程序内部会抛出AccessDeniedException错误。
捕获到AccessDeniedException错误之后。
默认是使用DelegatingAuthenticationEntryPoint实例的。commence方法。
里面会执行redirectStrategy.sendRedirect(request, response, redirectUrl)。
这个套的比较深,等我有能力叙述好了再展开。

我们再来看看登录机制是怎么实现的呢?
第一反应。filter处理。
那fillter在哪,我们点开formLogin()看看。
	public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
		return getOrApply(new FormLoginConfigurer<>());
	}
这里返回了一个FormLoginConfigurer,它是继承于AbstractAuthenticationFilterConfigurer。
它是继承于AbstractAuthenticationFilterConfigurer就是一个过滤的配置项。
我们点开new FormLoginConfigurer<>();
	public FormLoginConfigurer() {
		super(new UsernamePasswordAuthenticationFilter(), null);
		usernameParameter("username");
		passwordParameter("password");
	}
这里我们看到UsernamePasswordAuthenticationFilter,这个就是默认账号密码的filter。
我们先进入new UsernamePasswordAuthenticationFilter()看看。
    public UsernamePasswordAuthenticationFilter() {
		super(new AntPathRequestMatcher("/login", "POST"));
	}
这里看到了new AntPathRequestMatcher("/login", "POST")。
看啊!我们的login 地址出来了,同时是POST。
AntPathRequestMatcher是一个匹配器,当url为"/login" 方法为POST则会进行执行我们的过滤条件。
哪里用到呢?
UsernamePasswordAuthenticationFilter继承于AbstractAuthenticationProcessingFilter。
AbstractAuthenticationProcessingFilter有一个doFilter方法。
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
                //如果没有匹配到就执行接下来的Filter。
                //requiresAuthentication里面会使用到之前定义的AntPathRequestMatcher。
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);
			return;
		}
    	        ······省略
	}
doFilter会过滤自己没匹配到的请求,并继续执行接下来的Filter。
那如果我登录通过了,会怎么样呢,登录失败了,又会怎么样呢。?
我们接下来看doFilter方法。
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
	// 上面的省略。
		Authentication authResult;

		try {
		//执行attemptAuthentication方法面去获取到Authentication,里面包括请求得到的账号密码星信息。
			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);
		}
        
		successfulAuthentication(request, response, chain, authResult);
	}
从这里我们可以看到successfulAuthentication,和unsuccessfulAuthentication方法。
他们实际是去分别执行AuthenticationSuccessHandler接口的onAuthenticationSuccess
和AuthenticationFailureHandler接口的onAuthenticationFailure方法。
	protected void successfulAuthentication(HttpServletRequest request,
		HttpServletResponse response, FilterChain chain, Authentication authResult)
		throws IOException, ServletException {
                ······
		successHandler.onAuthenticationSuccess(request, response, authResult);
	}
	
	protected void unsuccessfulAuthentication(HttpServletRequest request,   
		HttpServletResponse response, AuthenticationException failed)
		throws IOException, ServletException {
        	······
		failureHandler.onAuthenticationFailure(request, response, failed);
	}
那这两个接口的实例是哪里来的呢?
我们思路回到FormLoginConfigurer初始化处。
	public FormLoginConfigurer() {
		super(new UsernamePasswordAuthenticationFilter(), null);
		usernameParameter("username");
		passwordParameter("password");
	}
这个类是集成于AbstractAuthenticationFilterConfigurer。
这个类的configure方法设置了
authFilter.setAuthenticationSuccessHandler(successHandler);
authFilter.setAuthenticationFailureHandler(failureHandler);
    @Override
	public void configure(B http) throws Exception {
	        ······
		authFilter.setAuthenticationSuccessHandler(successHandler);
		authFilter.setAuthenticationFailureHandler(failureHandler);
                ······
		F filter = postProcess(authFilter);
		http.addFilter(filter);
	}
这两个handle默认为new SavedRequestAwareAuthenticationSuccessHandler()
和new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl)
authenticationFailureUrl 默认为"/login?error"


第一部分我先这样过,我会进行修改排版和语言描述的。
现在还比较晦涩。