用掘金-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;
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 {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
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) {
unsuccessfulAuthentication(request, response, failed);
return;
}
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"
第一部分我先这样过,我会进行修改排版和语言描述的。
现在还比较晦涩。