😉😉 欢迎加入我们的学习交流群呀!
✅✅1:这是孙哥suns给大家的福利!
✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring等等很多应用和源码级别的高质量视频和笔记资料,你想学的我们这里都有!
🥭🥭3:QQ群:583783824 📚📚 工作微信:BigTreeJava 拉你进微信群,免费领取!
🍎🍎4:本文章内容出自上述:Spring应用课程!💞💞
💞💞5:以上内容,进群免费领取呦~ 💞💞💞💞
想要搞明白这个问题,我们需要复习一下SpringSecurity的30多个过滤器,其中标红的是启动时默认加载的一共有15个。这十五个当中和登录有关的一共有四个:UsernamePasswordAuthenticationFilter(处理表单登录)、DefaultLoginPageGeneratingFilter(配置登录页面)、ExceptionTranslationFilter(处理认证授权中的异常)、AuthorizationFilter(对请求进行访问权限处理)
其中粉色部分就是生成默认登录页面的区域,所以这块是我们需要着重研究的部分!
我们都知道,之前的文章中也都提到过,当我们在项目中引入SpringSecurity之后,所有的访问接口都会经过SringSecurity过滤器链条的拦截和处理!不论客户端发送的这个url请求在咱们的后台资源中是否存在,都必须经过这个认证检查。如果客户没哟U盾呢个路的话,就会进行强制登录那这个过程大概是个怎么过程呢?
大概的过程是这样的:
访问地址 http://localhost:800/hello。不管这个资源咱们的后台服务有没有,都会首先经过一堆的过滤器,这些个过滤器有Web服务自己定义的,有SpringSecurity自己加载的。
当请求到达AuthorizationFilter 时,检查发现用户未认证,请求被拦截,并抛出AccessDeniedException异常
抛出的 AccessDeniedException 异常会被 ExceptionTranslationFilter 捕获并启动身份验证
在这个 Filter中会调用LoginUrlAuthenticationEntryPoint的commence 方法,要求重定向到login页面
重定向到/login,也就是客户端发送login 请求/login 请求会被过滤器 DefaultLoginPageGeneratingFilter 拦截,并在过滤器中返回默认的登录页面
所以,真正做用于认证检查的过滤器是AuthorizationFilter这个过滤器。
我们知道,所有的重定向都是客户端需要重新发请求,这个时候客户端往后台重新发/login请求,此时呢第一个拦截器依旧不会来接,因为此时只是客户端发送了一个/login请求,而不是点击的账号密码的提交。这个时候请求顺利的到达DefaultLoginPageGeneratingFilter这个过滤器,在这个过滤器当中会生成登录页面并且返回到前端页面。
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { boolean loginError = this.isErrorPage(request); boolean logoutSuccess = this.isLogoutSuccess(request); if (!this.isLoginUrlRequest(request) && !loginError && !logoutSuccess) { chain.doFilter(request, response); } else { String loginPageHtml = this.generateLoginPageHtml(request, loginError, logoutSuccess); //设置响应内容格式 response.setContentType("text/html;charset=UTF-8"); //设置响应长度 response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length); //获取影响输出流,写到客户端浏览器。 response.getWriter().write(loginPageHtml); } } private String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) { String errorMsg = "Invalid credentials"; if (loginError) { HttpSession session = request.getSession(false); if (session != null) { AuthenticationException ex = (AuthenticationException)session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION"); errorMsg = ex != null ? ex.getMessage() : "Invalid credentials"; } } String contextPath = request.getContextPath(); StringBuilder sb = new StringBuilder(); sb.append("\n"); sb.append("\n"); sb.append(" \n"); sb.append(" \n"); sb.append(" \n"); sb.append(" \n"); sb.append(" \n"); sb.append("\n"); } Iterator var7; Map.Entry relyingPartyUrlToName; String url; String partyName; if (this.oauth2LoginEnabled) { sb.append("Please sign in \n"); sb.append(" \n"); sb.append(" \n"); sb.append(" \n"); sb.append(" \n"); sb.append(" \n"); if (this.formLoginEnabled) { sb.append("
"); url = (String)relyingPartyUrlToName.getKey(); sb.append(""); partyName = HtmlUtils.htmlEscape((String)relyingPartyUrlToName.getValue()); sb.append(partyName); sb.append(""); sb.append(" |
"); url = (String)relyingPartyUrlToName.getKey(); sb.append(""); partyName = HtmlUtils.htmlEscape((String)relyingPartyUrlToName.getValue()); sb.append(partyName); sb.append(""); sb.append(" |
这样,就完成了将默认的登录页面王客户端浏览器输出。这是硬生生的在Java中拼出了HTML和各种前端样式、JS功能,返回给浏览器,浏览器在进行解析,然后客户输入完毕之后,就可以进行验证了。